diff --git a/src/expr.rs b/src/expr.rs index 3371097d4..32a869c03 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -37,52 +37,1284 @@ pub enum SimpleExpr { Constant(Value), } -pub(crate) mod private { - use crate::{BinOper, LikeExpr, SimpleExpr, UnOper}; - - pub trait Expression: Sized { - fn un_op(self, o: UnOper) -> SimpleExpr; - - fn bin_op(self, op: O, right: T) -> SimpleExpr - where - O: Into, - T: Into; - - fn like_like(self, op: O, like: LikeExpr) -> SimpleExpr - where - O: Into, - { - self.bin_op( - op, - match like.escape { - Some(escape) => SimpleExpr::Binary( - Box::new(like.pattern.into()), - BinOper::Escape, - Box::new(SimpleExpr::Constant(escape.into())), - ), - None => like.pattern.into(), - }, - ) - } +/// "Operator" methods for building complex expressions. +/// +/// ## Examples +/// +/// ```no_run +/// use sea_query::*; +/// +/// let expr = 1_i32.cast_as(Alias::new("REAL")); +/// +/// let expr = Func::char_length("abc").eq(3_i32); +/// +/// let expr = Expr::current_date() +/// .cast_as(Alias::new("TEXT")) +/// .like("2024%"); +/// ``` +pub trait ExprTrait: Sized { + /// Express an arithmetic addition operation. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where(1.add(1).eq(2)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE 1 + 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 + 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 + 1 = 2"# + /// ); + /// ``` + fn add(self, right: R) -> SimpleExpr + where + R: Into, + { + ExprTrait::binary(self, BinOper::Add, right) + } + + /// Express a `AS enum` expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::insert() + /// .into_table(Char::Table) + /// .columns([Char::FontSize]) + /// .values_panic(["large".as_enum(Alias::new("FontSizeEnum"))]) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"INSERT INTO `character` (`font_size`) VALUES ('large')"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"INSERT INTO "character" ("font_size") VALUES (CAST('large' AS FontSizeEnum))"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"INSERT INTO "character" ("font_size") VALUES ('large')"# + /// ); + /// ``` + #[allow(clippy::wrong_self_convention)] + fn as_enum(self, type_name: N) -> SimpleExpr + where + N: IntoIden; + + fn and(self, right: R) -> SimpleExpr + where + R: Into, + { + ExprTrait::binary(self, BinOper::And, right) + } + + /// Express a `BETWEEN` expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::SizeW).into_column_ref().between(1, 10)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`size_w` BETWEEN 1 AND 10"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" BETWEEN 1 AND 10"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" BETWEEN 1 AND 10"# + /// ); + /// ``` + fn between(self, a: A, b: B) -> SimpleExpr + where + A: Into, + B: Into, + { + self.binary( + BinOper::Between, + SimpleExpr::Binary(Box::new(a.into()), BinOper::And, Box::new(b.into())), + ) + } + + /// Create any binary operation + /// + /// # Examples + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .cond_where(all![ + /// Char::SizeW.into_column_ref().binary(BinOper::SmallerThan, 10), + /// Char::SizeW.into_column_ref().binary(BinOper::GreaterThan, Char::SizeH.into_column_ref()) + /// ]) + /// .to_owned(); + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `size_w` < 10 AND `size_w` > `size_h`"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "size_w" < 10 AND "size_w" > "size_h""# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "size_w" < 10 AND "size_w" > "size_h""# + /// ); + /// ``` + fn binary(self, op: O, right: R) -> SimpleExpr + where + O: Into, + R: Into; + + /// Express a `CAST AS` expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .expr("1".cast_as(Alias::new("integer"))) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT CAST('1' AS integer)"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT CAST('1' AS integer)"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT CAST('1' AS integer)"# + /// ); + /// ``` + fn cast_as(self, type_name: N) -> SimpleExpr + where + N: IntoIden; + + /// Express an arithmetic division operation. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where(1.div(1).eq(2)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE 1 / 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 / 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 / 1 = 2"# + /// ); + /// ``` + fn div(self, right: R) -> SimpleExpr + where + R: Into, + { + ExprTrait::binary(self, BinOper::Div, right) + } + + /// Express an equal (`=`) expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// // Sometimes, you'll have to qualify the call because of conflicting std traits. + /// .and_where(ExprTrait::eq("What!", "Nothing")) + /// .and_where(Char::Id.into_column_ref().eq(1)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE 'What!' = 'Nothing' AND `id` = 1"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 'What!' = 'Nothing' AND "id" = 1"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 'What!' = 'Nothing' AND "id" = 1"# + /// ); + /// ``` + fn eq(self, right: R) -> SimpleExpr + where + R: Into, + { + ExprTrait::binary(self, BinOper::Equal, right) + } + + /// Express a equal expression between two table columns, + /// you will mainly use this to relate identical value between two table columns. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::FontId).into_column_ref().equals((Font::Table, Font::Id))) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`font_id` = `font`.`id`"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."font_id" = "font"."id""# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."font_id" = "font"."id""# + /// ); + /// ``` + fn equals(self, col: C) -> SimpleExpr + where + C: IntoColumnRef, + { + self.binary(BinOper::Equal, col.into_column_ref()) + } + + /// Express a greater than (`>`) expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::SizeW).into_column_ref().gt(2)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`size_w` > 2"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" > 2"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" > 2"# + /// ); + /// ``` + fn gt(self, right: R) -> SimpleExpr + where + R: Into, + { + ExprTrait::binary(self, BinOper::GreaterThan, right) + } + + /// Express a greater than or equal (`>=`) expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::SizeW).into_column_ref().gte(2)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`size_w` >= 2"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" >= 2"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" >= 2"# + /// ); + /// ``` + fn gte(self, right: R) -> SimpleExpr + where + R: Into, + { + ExprTrait::binary(self, BinOper::GreaterThanOrEqual, right) + } + + /// Express a `IN` sub-query expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where(Char::SizeW.into_column_ref().in_subquery( + /// Query::select() + /// .expr(Expr::cust("3 + 2 * 2")) + /// .take() + /// )) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `size_w` IN (SELECT 3 + 2 * 2)"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "size_w" IN (SELECT 3 + 2 * 2)"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "size_w" IN (SELECT 3 + 2 * 2)"# + /// ); + /// ``` + fn in_subquery(self, sel: SelectStatement) -> SimpleExpr { + self.binary( + BinOper::In, + SimpleExpr::SubQuery(None, Box::new(sel.into_sub_query_statement())), + ) + } + + /// Express a `IN` sub expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::FontId]) + /// .from(Char::Table) + /// .and_where( + /// ExprTrait::in_tuples( + /// Expr::tuple([ + /// Expr::col(Char::Character).into(), + /// Expr::col(Char::FontId).into(), + /// ]), + /// [(1, String::from("1")), (2, String::from("2"))] + /// ) + /// ) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `font_id` FROM `character` WHERE (`character`, `font_id`) IN ((1, '1'), (2, '2'))"# + /// ); + /// + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "font_id" FROM "character" WHERE ("character", "font_id") IN ((1, '1'), (2, '2'))"# + /// ); + /// + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "font_id" FROM "character" WHERE ("character", "font_id") IN ((1, '1'), (2, '2'))"# + /// ); + /// ``` + fn in_tuples(self, v: I) -> SimpleExpr + where + V: IntoValueTuple, + I: IntoIterator, + { + self.binary( + BinOper::In, + SimpleExpr::Tuple( + v.into_iter() + .map(|m| SimpleExpr::Values(m.into_value_tuple().into_iter().collect())) + .collect(), + ), + ) + } + + /// Express a `IS` expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::Ascii).into_column_ref().is(true)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`ascii` IS TRUE"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."ascii" IS TRUE"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."ascii" IS TRUE"# + /// ); + /// ``` + fn is(self, right: R) -> SimpleExpr + where + R: Into, + { + self.binary(BinOper::Is, right) + } + + /// Express a `IN` expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Id]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::SizeW).into_column_ref().is_in([1, 2, 3])) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `id` FROM `character` WHERE `character`.`size_w` IN (1, 2, 3)"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "id" FROM "character" WHERE "character"."size_w" IN (1, 2, 3)"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "id" FROM "character" WHERE "character"."size_w" IN (1, 2, 3)"# + /// ); + /// ``` + /// Empty value list + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Id]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::SizeW).into_column_ref().is_in(Vec::::new())) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `id` FROM `character` WHERE 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "id" FROM "character" WHERE 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "id" FROM "character" WHERE 1 = 2"# + /// ); + /// ``` + #[allow(clippy::wrong_self_convention)] + fn is_in(self, v: I) -> SimpleExpr + where + V: Into, + I: IntoIterator, + { + self.binary( + BinOper::In, + SimpleExpr::Tuple(v.into_iter().map(|v| v.into()).collect()), + ) + } + + /// Express a `IS NOT` expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::Ascii).into_column_ref().is_not(true)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`ascii` IS NOT TRUE"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."ascii" IS NOT TRUE"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."ascii" IS NOT TRUE"# + /// ); + /// ``` + #[allow(clippy::wrong_self_convention)] + fn is_not(self, right: R) -> SimpleExpr + where + R: Into, + { + self.binary(BinOper::IsNot, right) + } + + /// Express a `NOT IN` expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Id]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::SizeW).into_column_ref().is_not_in([1, 2, 3])) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `id` FROM `character` WHERE `character`.`size_w` NOT IN (1, 2, 3)"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "id" FROM "character" WHERE "character"."size_w" NOT IN (1, 2, 3)"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "id" FROM "character" WHERE "character"."size_w" NOT IN (1, 2, 3)"# + /// ); + /// ``` + /// Empty value list + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Id]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::SizeW).into_column_ref().is_not_in(Vec::::new())) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `id` FROM `character` WHERE 1 = 1"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "id" FROM "character" WHERE 1 = 1"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "id" FROM "character" WHERE 1 = 1"# + /// ); + /// ``` + #[allow(clippy::wrong_self_convention)] + fn is_not_in(self, v: I) -> SimpleExpr + where + V: Into, + I: IntoIterator, + { + self.binary( + BinOper::NotIn, + SimpleExpr::Tuple(v.into_iter().map(|v| v.into()).collect()), + ) + } + + /// Express a `IS NOT NULL` expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::SizeW).into_column_ref().is_not_null()) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`size_w` IS NOT NULL"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" IS NOT NULL"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" IS NOT NULL"# + /// ); + /// ``` + #[allow(clippy::wrong_self_convention)] + fn is_not_null(self) -> SimpleExpr { + self.binary(BinOper::IsNot, Keyword::Null) + } + + /// Express a `IS NULL` expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::SizeW).into_column_ref().is_null()) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`size_w` IS NULL"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" IS NULL"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" IS NULL"# + /// ); + /// ``` + #[allow(clippy::wrong_self_convention)] + fn is_null(self) -> SimpleExpr { + self.binary(BinOper::Is, Keyword::Null) + } + + /// Express a bitwise left shift. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where(1.left_shift(1).eq(2)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE 1 << 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 << 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 << 1 = 2"# + /// ); + /// ``` + fn left_shift(self, right: R) -> SimpleExpr + where + R: Into, + { + self.binary(BinOper::LShift, right) + } + + /// Express a `LIKE` expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::Character).into_column_ref().like("Ours'%")) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`character` LIKE 'Ours\'%'"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."character" LIKE E'Ours\'%'"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."character" LIKE 'Ours''%'"# + /// ); + /// ``` + /// + /// Like with ESCAPE + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::Character).into_column_ref().like(LikeExpr::new(r"|_Our|_").escape('|'))) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`character` LIKE '|_Our|_' ESCAPE '|'"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."character" LIKE '|_Our|_' ESCAPE '|'"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."character" LIKE '|_Our|_' ESCAPE '|'"# + /// ); + /// ``` + fn like(self, like: L) -> SimpleExpr + where + L: IntoLikeExpr, + { + ExprTrait::binary(self, BinOper::Like, like.into_like_expr()) + } + + /// Express a less than (`<`) expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::SizeW).into_column_ref().lt(2)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`size_w` < 2"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" < 2"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" < 2"# + /// ); + /// ``` + fn lt(self, right: R) -> SimpleExpr + where + R: Into, + { + ExprTrait::binary(self, BinOper::SmallerThan, right) + } + + /// Express a less than or equal (`<=`) expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::SizeW).into_column_ref().lte(2)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`size_w` <= 2"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" <= 2"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" <= 2"# + /// ); + /// ``` + fn lte(self, right: R) -> SimpleExpr + where + R: Into, + { + ExprTrait::binary(self, BinOper::SmallerThanOrEqual, right) + } + + /// Express an arithmetic modulo operation. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where(1.modulo(1).eq(2)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE 1 % 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 % 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 % 1 = 2"# + /// ); + /// ``` + fn modulo(self, right: R) -> SimpleExpr + where + R: Into, + { + self.binary(BinOper::Mod, right) + } + + /// Express an arithmetic multiplication operation. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where(1.mul(1).eq(2)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE 1 * 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 * 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 * 1 = 2"# + /// ); + /// ``` + fn mul(self, right: R) -> SimpleExpr + where + R: Into, + { + ExprTrait::binary(self, BinOper::Mul, right) + } + + /// Express a not equal (`<>`) expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// // Sometimes, you'll have to qualify the call because of conflicting std traits. + /// .and_where(ExprTrait::ne("Morning", "Good")) + /// .and_where(Char::Id.into_column_ref().ne(1)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE 'Morning' <> 'Good' AND `id` <> 1"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 'Morning' <> 'Good' AND "id" <> 1"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 'Morning' <> 'Good' AND "id" <> 1"# + /// ); + /// ``` + fn ne(self, right: R) -> SimpleExpr + where + R: Into, + { + ExprTrait::binary(self, BinOper::NotEqual, right) + } + + /// Negates an expression with `NOT`. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where(ExprTrait::not(Expr::col((Char::Table, Char::SizeW)).is_null())) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE NOT `character`.`size_w` IS NULL"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE NOT "character"."size_w" IS NULL"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE NOT "character"."size_w" IS NULL"# + /// ); + /// ``` + fn not(self) -> SimpleExpr { + self.unary(UnOper::Not) + } + + /// Express a `NOT BETWEEN` expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::SizeW).into_column_ref().not_between(1, 10)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`size_w` NOT BETWEEN 1 AND 10"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" NOT BETWEEN 1 AND 10"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."size_w" NOT BETWEEN 1 AND 10"# + /// ); + /// ``` + fn not_between(self, a: A, b: B) -> SimpleExpr + where + A: Into, + B: Into, + { + self.binary( + BinOper::NotBetween, + SimpleExpr::Binary(Box::new(a.into()), BinOper::And, Box::new(b.into())), + ) + } + + /// Express a not equal expression between two table columns, + /// you will mainly use this to relate identical value between two table columns. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::FontId).into_column_ref().not_equals((Font::Table, Font::Id))) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`font_id` <> `font`.`id`"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."font_id" <> "font"."id""# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."font_id" <> "font"."id""# + /// ); + /// ``` + fn not_equals(self, col: C) -> SimpleExpr + where + C: IntoColumnRef, + { + self.binary(BinOper::NotEqual, col.into_column_ref()) + } + + /// Express a `NOT IN` sub-query expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where(Char::SizeW.into_column_ref().not_in_subquery( + /// Query::select() + /// .expr(Expr::cust("3 + 2 * 2")) + /// .take() + /// )) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `size_w` NOT IN (SELECT 3 + 2 * 2)"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "size_w" NOT IN (SELECT 3 + 2 * 2)"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "size_w" NOT IN (SELECT 3 + 2 * 2)"# + /// ); + /// ``` + fn not_in_subquery(self, sel: SelectStatement) -> SimpleExpr { + self.binary( + BinOper::NotIn, + SimpleExpr::SubQuery(None, Box::new(sel.into_sub_query_statement())), + ) + } + + /// Express a `NOT LIKE` expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where((Char::Table, Char::Character).into_column_ref().not_like("Ours'%")) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE `character`.`character` NOT LIKE 'Ours\'%'"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."character" NOT LIKE E'Ours\'%'"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."character" NOT LIKE 'Ours''%'"# + /// ); + /// ``` + fn not_like(self, like: L) -> SimpleExpr + where + L: IntoLikeExpr, + { + ExprTrait::binary(self, BinOper::NotLike, like.into_like_expr()) + } + + /// Express a logical `OR` operation. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where(false.or(true)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE FALSE OR TRUE"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE FALSE OR TRUE"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE FALSE OR TRUE"# + /// ); + /// ``` + fn or(self, right: R) -> SimpleExpr + where + R: Into, + { + ExprTrait::binary(self, BinOper::Or, right) + } + + /// Express a bitwise right shift. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where(1.right_shift(1).eq(2)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE 1 >> 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 >> 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 >> 1 = 2"# + /// ); + /// ``` + fn right_shift(self, right: R) -> SimpleExpr + where + R: Into, + { + self.binary(BinOper::RShift, right) + } + + /// Express an arithmetic subtraction operation. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{tests_cfg::*, *}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where(1.sub(1).eq(2)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE 1 - 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 - 1 = 2"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE 1 - 1 = 2"# + /// ); + /// ``` + fn sub(self, right: R) -> SimpleExpr + where + R: Into, + { + ExprTrait::binary(self, BinOper::Sub, right) } -} -use private::Expression; + /// Apply any unary operator to the expression. + /// + /// # Examples + /// + /// ``` + /// use sea_query::{*, tests_cfg::*}; + /// + /// let query = Query::select() + /// .columns([Char::Character, Char::SizeW, Char::SizeH]) + /// .from(Char::Table) + /// .and_where(Expr::col((Char::Table, Char::SizeW)).is_null().unary(UnOper::Not)) + /// .to_owned(); + /// + /// assert_eq!( + /// query.to_string(MysqlQueryBuilder), + /// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE NOT `character`.`size_w` IS NULL"# + /// ); + /// assert_eq!( + /// query.to_string(PostgresQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE NOT "character"."size_w" IS NULL"# + /// ); + /// assert_eq!( + /// query.to_string(SqliteQueryBuilder), + /// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE NOT "character"."size_w" IS NULL"# + /// ); + /// ``` + fn unary(self, o: UnOper) -> SimpleExpr; +} -impl Expression for Expr { - fn un_op(mut self, o: UnOper) -> SimpleExpr { - self.uopr = Some(o); - self.into() +/// This generic implementation covers all expression types, +/// including [ColumnRef], [Value], [FunctionCall], [SimpleExpr]... +impl ExprTrait for T +where + T: Into, +{ + fn as_enum(self, type_name: N) -> SimpleExpr + where + N: IntoIden, + { + SimpleExpr::AsEnum(type_name.into_iden(), Box::new(self.into())) } - fn bin_op(mut self, op: O, right: T) -> SimpleExpr + fn binary(self, op: O, right: R) -> SimpleExpr where O: Into, - T: Into, + R: Into, + { + SimpleExpr::Binary(Box::new(self.into()), op.into(), Box::new(right.into())) + } + + fn cast_as(self, type_name: N) -> SimpleExpr + where + N: IntoIden, { - self.bopr = Some(op.into()); - self.right = Some(right.into()); - self.into() + SimpleExpr::FunctionCall(Func::cast_as(self, type_name)) + } + + fn unary(self, op: UnOper) -> SimpleExpr { + SimpleExpr::Unary(op, Box::new(self.into())) } } @@ -503,6 +1735,8 @@ impl Expr { /// Express an equal (`=`) expression. /// + /// This is equivalent to a newer [ExprTrait::eq] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -532,11 +1766,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::Equal, v) + ExprTrait::eq(self, v) } /// Express a not equal (`<>`) expression. /// + /// This is equivalent to a newer [ExprTrait::ne] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -566,12 +1802,14 @@ impl Expr { where V: Into, { - self.binary(BinOper::NotEqual, v) + ExprTrait::ne(self, v) } /// Express a equal expression between two table columns, /// you will mainly use this to relate identical value between two table columns. /// + /// This is equivalent to a newer [ExprTrait::equals] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -600,12 +1838,14 @@ impl Expr { where C: IntoColumnRef, { - self.binary(BinOper::Equal, col.into_column_ref()) + ExprTrait::equals(self, col) } /// Express a not equal expression between two table columns, /// you will mainly use this to relate identical value between two table columns. /// + /// This is equivalent to a newer [ExprTrait::not_equals] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -634,11 +1874,13 @@ impl Expr { where C: IntoColumnRef, { - self.binary(BinOper::NotEqual, col.into_column_ref()) + ExprTrait::not_equals(self, col) } /// Express a greater than (`>`) expression. /// + /// This is equivalent to a newer [ExprTrait::gt] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -667,11 +1909,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::GreaterThan, v.into()) + ExprTrait::gt(self, v) } /// Express a greater than or equal (`>=`) expression. /// + /// This is equivalent to a newer [ExprTrait::gte] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -700,11 +1944,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::GreaterThanOrEqual, v) + ExprTrait::gte(self, v) } /// Express a less than (`<`) expression. /// + /// This is equivalent to a newer [ExprTrait::lt] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -733,11 +1979,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::SmallerThan, v) + ExprTrait::lt(self, v) } /// Express a less than or equal (`<=`) expression. /// + /// This is equivalent to a newer [ExprTrait::lte] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -766,11 +2014,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::SmallerThanOrEqual, v) + ExprTrait::lte(self, v) } /// Express an arithmetic addition operation. /// + /// This is equivalent to a newer [ExprTrait::add] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -800,11 +2050,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::Add, v) + ExprTrait::add(self, v) } /// Express an arithmetic subtraction operation. /// + /// This is equivalent to a newer [ExprTrait::sub] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -834,11 +2086,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::Sub, v) + ExprTrait::sub(self, v) } /// Express an arithmetic multiplication operation. /// + /// This is equivalent to a newer [ExprTrait::mul] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -868,11 +2122,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::Mul, v) + ExprTrait::mul(self, v) } /// Express an arithmetic division operation. /// + /// This is equivalent to a newer [ExprTrait::div] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -902,11 +2158,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::Div, v) + ExprTrait::div(self, v) } /// Express an arithmetic modulo operation. /// + /// This is equivalent to a newer [ExprTrait::modulo] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -936,11 +2194,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::Mod, v) + ExprTrait::modulo(self, v) } /// Express a bitwise left shift. /// + /// This is equivalent to a newer [ExprTrait::left_shift] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -970,11 +2230,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::LShift, v) + ExprTrait::left_shift(self, v) } /// Express a bitwise right shift. /// + /// This is equivalent to a newer [ExprTrait::right_shift] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1004,11 +2266,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::RShift, v) + ExprTrait::right_shift(self, v) } /// Express a `BETWEEN` expression. /// + /// This is equivalent to a newer [ExprTrait::between] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1037,11 +2301,13 @@ impl Expr { where V: Into, { - self.between_or_not_between(BinOper::Between, a, b) + ExprTrait::between(self, a, b) } /// Express a `NOT BETWEEN` expression. /// + /// This is equivalent to a newer [ExprTrait::not_between] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1070,21 +2336,13 @@ impl Expr { where V: Into, { - self.between_or_not_between(BinOper::NotBetween, a, b) - } - - fn between_or_not_between(self, op: BinOper, a: V, b: V) -> SimpleExpr - where - V: Into, - { - self.binary( - op, - SimpleExpr::Binary(Box::new(a.into()), BinOper::And, Box::new(b.into())), - ) + ExprTrait::not_between(self, a, b) } /// Express a `LIKE` expression. /// + /// This is equivalent to a newer [ExprTrait::like] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1135,16 +2393,20 @@ impl Expr { /// ); /// ``` pub fn like(self, like: L) -> SimpleExpr { - self.like_like(BinOper::Like, like.into_like_expr()) + ExprTrait::like(self, like) } - /// Express a `NOT LIKE` expression + /// Express a `NOT LIKE` expression. + /// + /// This is equivalent to a newer [ExprTrait::not_like] and may require more some wrapping beforehand. pub fn not_like(self, like: L) -> SimpleExpr { - self.like_like(BinOper::NotLike, like.into_like_expr()) + ExprTrait::not_like(self, like) } /// Express a `IS NULL` expression. /// + /// This is equivalent to a newer [ExprTrait::is_null] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1171,11 +2433,13 @@ impl Expr { /// ``` #[allow(clippy::wrong_self_convention)] pub fn is_null(self) -> SimpleExpr { - self.binary(BinOper::Is, Keyword::Null) + ExprTrait::is_null(self) } /// Express a `IS` expression. /// + /// This is equivalent to a newer [ExprTrait::is] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1204,11 +2468,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::Is, v) + ExprTrait::is(self, v) } /// Express a `IS NOT NULL` expression. /// + /// This is equivalent to a newer [ExprTrait::is_not_null] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1235,11 +2501,13 @@ impl Expr { /// ``` #[allow(clippy::wrong_self_convention)] pub fn is_not_null(self) -> SimpleExpr { - self.binary(BinOper::IsNot, Keyword::Null) + ExprTrait::is_not_null(self) } /// Express a `IS NOT` expression. /// + /// This is equivalent to a newer [ExprTrait::is_not] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1268,11 +2536,13 @@ impl Expr { where V: Into, { - self.binary(BinOper::IsNot, v) + ExprTrait::is_not(self, v) } /// Create any binary operation /// + /// This is equivalent to a newer [ExprTrait::binary] and may require more some wrapping beforehand. + /// /// # Examples /// ``` /// use sea_query::{*, tests_cfg::*}; @@ -1303,11 +2573,13 @@ impl Expr { O: Into, T: Into, { - self.bin_op(op, right) + ExprTrait::binary(self, op, right) } /// Negates an expression with `NOT`. /// + /// This is equivalent to a newer [ExprTrait::not] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1334,7 +2606,7 @@ impl Expr { /// ``` #[allow(clippy::should_implement_trait)] pub fn not(self) -> SimpleExpr { - self.un_op(UnOper::Not) + ExprTrait::not(self) } /// Express a `MAX` function. @@ -1516,6 +2788,8 @@ impl Expr { /// Express a `IN` expression. /// + /// This is equivalent to a newer [ExprTrait::is_in] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1564,18 +2838,18 @@ impl Expr { /// ); /// ``` #[allow(clippy::wrong_self_convention)] - pub fn is_in(mut self, v: I) -> SimpleExpr + pub fn is_in(self, v: I) -> SimpleExpr where V: Into, I: IntoIterator, { - self.bopr = Some(BinOper::In); - self.right = Some(SimpleExpr::Tuple(v.into_iter().map(|v| v.into()).collect())); - self.into() + ExprTrait::is_in(self, v) } /// Express a `IN` sub expression. /// + /// This is equivalent to a newer [ExprTrait::in_tuples] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1609,22 +2883,18 @@ impl Expr { /// ); /// ``` #[allow(clippy::wrong_self_convention)] - pub fn in_tuples(mut self, v: I) -> SimpleExpr + pub fn in_tuples(self, v: I) -> SimpleExpr where V: IntoValueTuple, I: IntoIterator, { - self.bopr = Some(BinOper::In); - self.right = Some(SimpleExpr::Tuple( - v.into_iter() - .map(|m| SimpleExpr::Values(m.into_value_tuple().into_iter().collect())) - .collect(), - )); - self.into() + ExprTrait::in_tuples(self, v) } /// Express a `NOT IN` expression. /// + /// This is equivalent to a newer [ExprTrait::is_not_in] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1673,18 +2943,18 @@ impl Expr { /// ); /// ``` #[allow(clippy::wrong_self_convention)] - pub fn is_not_in(mut self, v: I) -> SimpleExpr + pub fn is_not_in(self, v: I) -> SimpleExpr where V: Into, I: IntoIterator, { - self.bopr = Some(BinOper::NotIn); - self.right = Some(SimpleExpr::Tuple(v.into_iter().map(|v| v.into()).collect())); - self.into() + ExprTrait::is_not_in(self, v) } /// Express a `IN` sub-query expression. /// + /// This is equivalent to a newer [ExprTrait::in_subquery] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1714,17 +2984,14 @@ impl Expr { /// ); /// ``` #[allow(clippy::wrong_self_convention)] - pub fn in_subquery(mut self, sel: SelectStatement) -> SimpleExpr { - self.bopr = Some(BinOper::In); - self.right = Some(SimpleExpr::SubQuery( - None, - Box::new(sel.into_sub_query_statement()), - )); - self.into() + pub fn in_subquery(self, sel: SelectStatement) -> SimpleExpr { + ExprTrait::in_subquery(self, sel) } /// Express a `NOT IN` sub-query expression. /// + /// This is equivalent to a newer [ExprTrait::not_in_subquery] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1754,13 +3021,8 @@ impl Expr { /// ); /// ``` #[allow(clippy::wrong_self_convention)] - pub fn not_in_subquery(mut self, sel: SelectStatement) -> SimpleExpr { - self.bopr = Some(BinOper::NotIn); - self.right = Some(SimpleExpr::SubQuery( - None, - Box::new(sel.into_sub_query_statement()), - )); - self.into() + pub fn not_in_subquery(self, sel: SelectStatement) -> SimpleExpr { + ExprTrait::not_in_subquery(self, sel) } /// Express a `EXISTS` sub-query expression. @@ -1867,6 +3129,8 @@ impl Expr { /// Express a `AS enum` expression. /// + /// This is equivalent to a newer [ExprTrait::as_enum] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1913,7 +3177,7 @@ impl Expr { where T: IntoIden, { - SimpleExpr::AsEnum(type_name.into_iden(), Box::new(self.into())) + ExprTrait::as_enum(self, type_name) } /// Adds new `CASE WHEN` to existing case statement. @@ -1950,6 +3214,8 @@ impl Expr { /// Express a `CAST AS` expression. /// + /// This is equivalent to a newer [ExprTrait::cast_as] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -1976,8 +3242,7 @@ impl Expr { where T: IntoIden, { - let func = Func::cast_as(self, type_name); - SimpleExpr::FunctionCall(func) + ExprTrait::cast_as(self, type_name) } /// Keyword `CURRENT_DATE`. @@ -2115,23 +3380,24 @@ impl From for SimpleExpr { } } -impl Expression for SimpleExpr { - fn un_op(self, op: UnOper) -> SimpleExpr { - SimpleExpr::Unary(op, Box::new(self)) - } - - fn bin_op(self, op: O, right: T) -> SimpleExpr - where - O: Into, - T: Into, - { - SimpleExpr::Binary(Box::new(self), op.into(), Box::new(right.into())) +impl From for SimpleExpr { + fn from(like: LikeExpr) -> Self { + match like.escape { + Some(escape) => SimpleExpr::Binary( + Box::new(like.pattern.into()), + BinOper::Escape, + Box::new(SimpleExpr::Constant(escape.into())), + ), + None => like.pattern.into(), + } } } impl SimpleExpr { /// Negates an expression with `NOT`. /// + /// This is equivalent to a newer [ExprTrait::not] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -2158,7 +3424,7 @@ impl SimpleExpr { /// ``` #[allow(clippy::should_implement_trait)] pub fn not(self) -> SimpleExpr { - self.un_op(UnOper::Not) + ExprTrait::not(self) } /// Express a logical `AND` operation. @@ -2191,11 +3457,13 @@ impl SimpleExpr { /// ); /// ``` pub fn and(self, right: SimpleExpr) -> Self { - self.binary(BinOper::And, right) + ExprTrait::and(self, right) } /// Express a logical `OR` operation. /// + /// This is equivalent to a newer [ExprTrait::or] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -2222,11 +3490,13 @@ impl SimpleExpr { /// ); /// ``` pub fn or(self, right: SimpleExpr) -> Self { - self.binary(BinOper::Or, right) + ExprTrait::or(self, right) } /// Express an equal (`=`) expression. /// + /// This is equivalent to a newer [ExprTrait::eq] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -2255,11 +3525,13 @@ impl SimpleExpr { where V: Into, { - self.binary(BinOper::Equal, v) + ExprTrait::eq(self, v) } /// Express a not equal (`<>`) expression. /// + /// This is equivalent to a newer [ExprTrait::ne] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -2288,11 +3560,13 @@ impl SimpleExpr { where V: Into, { - self.binary(BinOper::NotEqual, v) + ExprTrait::ne(self, v) } /// Perform addition with another [`SimpleExpr`]. /// + /// This is equivalent to a newer [ExprTrait::add] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -2325,11 +3599,13 @@ impl SimpleExpr { where T: Into, { - self.binary(BinOper::Add, right) + ExprTrait::add(self, right) } /// Perform multiplication with another [`SimpleExpr`]. /// + /// This is equivalent to a newer [ExprTrait::mul] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -2362,11 +3638,13 @@ impl SimpleExpr { where T: Into, { - self.binary(BinOper::Mul, right.into()) + ExprTrait::mul(self, right) } /// Perform division with another [`SimpleExpr`]. /// + /// This is equivalent to a newer [ExprTrait::div] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -2399,11 +3677,13 @@ impl SimpleExpr { where T: Into, { - self.binary(BinOper::Div, right.into()) + ExprTrait::div(self, right) } /// Perform subtraction with another [`SimpleExpr`]. /// + /// This is equivalent to a newer [ExprTrait::sub] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -2436,11 +3716,13 @@ impl SimpleExpr { where T: Into, { - self.binary(BinOper::Sub, right) + ExprTrait::sub(self, right) } /// Express a `CAST AS` expression. /// + /// This is equivalent to a newer [ExprTrait::cast_as] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -2467,8 +3749,7 @@ impl SimpleExpr { where T: IntoIden, { - let func = Func::cast_as(self, type_name); - Self::FunctionCall(func) + ExprTrait::cast_as(self, type_name) } /// Express a case-sensitive `CAST AS` expression. @@ -2505,6 +3786,8 @@ impl SimpleExpr { /// Create any binary operation /// + /// This is equivalent to a newer [ExprTrait::add] and may require more some wrapping beforehand. + /// /// # Examples /// ``` /// use sea_query::{*, tests_cfg::*}; @@ -2534,11 +3817,13 @@ impl SimpleExpr { O: Into, T: Into, { - self.bin_op(op, right) + ExprTrait::binary(self, op, right) } /// Express a `LIKE` expression. /// + /// This is equivalent to a newer [ExprTrait::like] and may require more some wrapping beforehand. + /// /// # Examples /// /// ``` @@ -2564,12 +3849,14 @@ impl SimpleExpr { /// ); /// ``` pub fn like(self, like: L) -> Self { - self.like_like(BinOper::Like, like.into_like_expr()) + ExprTrait::like(self, like) } - /// Express a `NOT LIKE` expression + /// Express a `NOT LIKE` expression. + /// + /// This is equivalent to a newer [ExprTrait::not_like] and may require more some wrapping beforehand. pub fn not_like(self, like: L) -> Self { - self.like_like(BinOper::NotLike, like.into_like_expr()) + ExprTrait::not_like(self, like) } pub(crate) fn is_binary(&self) -> bool { diff --git a/src/extension/postgres/expr.rs b/src/extension/postgres/expr.rs index a8ca29282..8c4e89421 100644 --- a/src/extension/postgres/expr.rs +++ b/src/extension/postgres/expr.rs @@ -1,7 +1,9 @@ use super::PgBinOper; -use crate::{expr::private::Expression, Expr, IntoLikeExpr, SimpleExpr}; +use crate::{ + ColumnRef, Expr, ExprTrait, FunctionCall, IntoLikeExpr, Keyword, LikeExpr, SimpleExpr, Value, +}; -pub trait PgExpr: Expression { +pub trait PgExpr: ExprTrait { /// Express an postgres concatenate (`||`) expression. /// /// # Examples @@ -24,7 +26,7 @@ pub trait PgExpr: Expression { where T: Into, { - self.bin_op(PgBinOper::Concatenate, right) + self.binary(PgBinOper::Concatenate, right) } /// Alias of [`PgExpr::concatenate`] fn concat(self, right: T) -> SimpleExpr @@ -57,7 +59,7 @@ pub trait PgExpr: Expression { where T: Into, { - self.bin_op(PgBinOper::Matches, expr) + self.binary(PgBinOper::Matches, expr) } /// Express an postgres fulltext search contains (`@>`) expression. @@ -83,7 +85,7 @@ pub trait PgExpr: Expression { where T: Into, { - self.bin_op(PgBinOper::Contains, expr) + self.binary(PgBinOper::Contains, expr) } /// Express an postgres fulltext search contained (`<@`) expression. @@ -109,7 +111,7 @@ pub trait PgExpr: Expression { where T: Into, { - self.bin_op(PgBinOper::Contained, expr) + self.binary(PgBinOper::Contained, expr) } /// Express a `ILIKE` expression. @@ -134,7 +136,7 @@ pub trait PgExpr: Expression { where L: IntoLikeExpr, { - self.like_like(PgBinOper::ILike, like.into_like_expr()) + self.binary(PgBinOper::ILike, like.into_like_expr()) } /// Express a `NOT ILIKE` expression @@ -142,7 +144,7 @@ pub trait PgExpr: Expression { where L: IntoLikeExpr, { - self.like_like(PgBinOper::NotILike, like.into_like_expr()) + self.binary(PgBinOper::NotILike, like.into_like_expr()) } /// Express a postgres retrieves JSON field as JSON value (`->`). @@ -167,7 +169,7 @@ pub trait PgExpr: Expression { where T: Into, { - self.bin_op(PgBinOper::GetJsonField, right) + self.binary(PgBinOper::GetJsonField, right) } /// Express a postgres retrieves JSON field and casts it to an appropriate SQL type (`->>`). @@ -192,10 +194,16 @@ pub trait PgExpr: Expression { where T: Into, { - self.bin_op(PgBinOper::CastJsonField, right) + self.binary(PgBinOper::CastJsonField, right) } } +// TODO: https://github.com/SeaQL/sea-query/discussions/795: +// replace all of this with `impl PgExpr for T where T: ExprTrait {}` impl PgExpr for Expr {} - impl PgExpr for SimpleExpr {} +impl PgExpr for FunctionCall {} +impl PgExpr for ColumnRef {} +impl PgExpr for Keyword {} +impl PgExpr for LikeExpr {} +impl PgExpr for Value {} diff --git a/src/extension/sqlite/expr.rs b/src/extension/sqlite/expr.rs index 1623f5c59..738fff4f5 100644 --- a/src/extension/sqlite/expr.rs +++ b/src/extension/sqlite/expr.rs @@ -1,8 +1,8 @@ -use crate::{expr::private::Expression, Expr, SimpleExpr}; +use crate::{ColumnRef, Expr, ExprTrait, FunctionCall, Keyword, LikeExpr, SimpleExpr, Value}; use super::SqliteBinOper; -pub trait SqliteExpr: Expression { +pub trait SqliteExpr: ExprTrait { /// Express an sqlite `GLOB` operator. /// /// # Examples @@ -25,7 +25,7 @@ pub trait SqliteExpr: Expression { where T: Into, { - self.bin_op(SqliteBinOper::Glob, right) + self.binary(SqliteBinOper::Glob, right) } /// Express an sqlite `MATCH` operator. @@ -50,7 +50,7 @@ pub trait SqliteExpr: Expression { where T: Into, { - self.bin_op(SqliteBinOper::Match, right) + self.binary(SqliteBinOper::Match, right) } /// Express an sqlite retrieves JSON field as JSON value (`->`). @@ -75,7 +75,7 @@ pub trait SqliteExpr: Expression { where T: Into, { - self.bin_op(SqliteBinOper::GetJsonField, right) + self.binary(SqliteBinOper::GetJsonField, right) } /// Express an sqlite retrieves JSON field and casts it to an appropriate SQL type (`->>`). @@ -100,10 +100,16 @@ pub trait SqliteExpr: Expression { where T: Into, { - self.bin_op(SqliteBinOper::CastJsonField, right) + self.binary(SqliteBinOper::CastJsonField, right) } } +// TODO: https://github.com/SeaQL/sea-query/discussions/795: +// replace all of this with `impl PgExpr for T where T: ExprTrait {}` impl SqliteExpr for Expr {} - impl SqliteExpr for SimpleExpr {} +impl SqliteExpr for FunctionCall {} +impl SqliteExpr for ColumnRef {} +impl SqliteExpr for Keyword {} +impl SqliteExpr for LikeExpr {} +impl SqliteExpr for Value {}