Skip to content
15 changes: 11 additions & 4 deletions src/expr/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -868,13 +868,20 @@ impl From<Keyword> for Expr {

impl From<LikeExpr> for Expr {
fn from(like: LikeExpr) -> Self {
match like.escape {
Some(escape) => Self::Binary(
Box::new(like.pattern.into()),
match like.0 {
crate::LikeExprInner::Str {
pattern,
escape: Some(escape),
} => Self::Binary(
Box::new(pattern.into()),
BinOper::Escape,
Box::new(Expr::Constant(escape.into())),
),
None => like.pattern.into(),
crate::LikeExprInner::Str {
pattern,
escape: None,
} => pattern.into(),
crate::LikeExprInner::Expr(expr) => expr,
}
}
}
Expand Down
47 changes: 34 additions & 13 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,39 +169,48 @@ pub enum Keyword {
Custom(DynIden),
}

/// Like Expression
/// Like Expression.
///
/// Wraps [`LikeExprInner`] to keep enum variants private, since Rust enum
/// variants are always public.
#[derive(Debug, Clone)]
pub struct LikeExpr(pub(crate) LikeExprInner);

#[derive(Debug, Clone)]
pub struct LikeExpr {
pub(crate) pattern: String,
pub(crate) escape: Option<char>,
pub(crate) enum LikeExprInner {
Str {
pattern: String,
escape: Option<char>,
},
Expr(crate::Expr),
}

impl LikeExpr {
pub fn new<T>(pattern: T) -> Self
where
T: Into<String>,
{
Self {
Self(LikeExprInner::Str {
pattern: pattern.into(),
escape: None,
}
})
}

#[deprecated(since = "0.29.0", note = "Please use the [`LikeExpr::new`] method")]
pub fn str<T>(pattern: T) -> Self
where
T: Into<String>,
{
Self {
pattern: pattern.into(),
escape: None,
}
LikeExpr::new(pattern)
}

pub fn escape(self, c: char) -> Self {
Comment thread
rahuld109 marked this conversation as resolved.
Self {
pattern: self.pattern,
escape: Some(c),
match self.0 {
LikeExprInner::Str { pattern, .. } => Self(LikeExprInner::Str {
pattern,
escape: Some(c),
}),
LikeExprInner::Expr(_) => self,
}
}
}
Expand All @@ -228,6 +237,18 @@ where
}
}

impl From<crate::Expr> for LikeExpr {
fn from(expr: crate::Expr) -> Self {
Self(LikeExprInner::Expr(expr))
}
}

impl From<crate::FunctionCall> for LikeExpr {
fn from(func: crate::FunctionCall) -> Self {
Self(LikeExprInner::Expr(crate::Expr::FunctionCall(func)))
}
}

/// SubQuery operators
#[derive(Debug, Copy, Clone, PartialEq)]
#[non_exhaustive]
Expand Down
39 changes: 39 additions & 0 deletions tests/mysql/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1553,3 +1553,42 @@ fn sub_query_with_fn() {
"SELECT ARRAY((SELECT * FROM `character`))"
);
}

#[test]
fn select_like_with_expr() {
assert_eq!(
Query::select()
.column(Char::Character)
.from(Char::Table)
.and_where(Expr::col(Char::Character).like(Expr::col(Char::FontId)))
.to_string(MysqlQueryBuilder),
r#"SELECT `character` FROM `character` WHERE `character` LIKE `font_id`"#
);
}

#[test]
fn select_like_with_function() {
assert_eq!(
Query::select()
.column(Char::Character)
.from(Char::Table)
.and_where(
Expr::expr(Func::lower(Expr::col(Char::Character)))
.like(Func::lower(Expr::col(Char::FontId)))
)
.to_string(MysqlQueryBuilder),
r#"SELECT `character` FROM `character` WHERE LOWER(`character`) LIKE LOWER(`font_id`)"#
);
}

#[test]
fn select_not_like_with_expr() {
assert_eq!(
Query::select()
.column(Char::Character)
.from(Char::Table)
.and_where(Expr::col(Char::Character).not_like(Expr::col(Char::FontId)))
.to_string(MysqlQueryBuilder),
r#"SELECT `character` FROM `character` WHERE `character` NOT LIKE `font_id`"#
);
}
64 changes: 64 additions & 0 deletions tests/postgres/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use core::f64;

use super::*;
use pretty_assertions::assert_eq;
use sea_query::extension::postgres::PgExpr;
use sea_query::{audit::AuditTrait, extension::postgres::PgBinOper};

#[test]
Expand Down Expand Up @@ -2525,3 +2526,66 @@ fn test_pgvector_select() {
r#"SELECT "character" FROM "character" WHERE "character" = '[1,2]'"#
);
}

#[test]
fn select_like_with_expr() {
Comment thread
rahuld109 marked this conversation as resolved.
Outdated
assert_eq!(
Query::select()
.column(Char::Character)
.from(Char::Table)
.and_where(Expr::col(Char::Character).like(Expr::col(Char::FontId)))
.to_string(PostgresQueryBuilder),
r#"SELECT "character" FROM "character" WHERE "character" LIKE "font_id""#
);
}

#[test]
fn select_like_with_function() {
assert_eq!(
Query::select()
.column(Char::Character)
.from(Char::Table)
.and_where(
Expr::expr(Func::lower(Expr::col(Char::Character)))
.like(Func::lower(Expr::col(Char::FontId)))
)
.to_string(PostgresQueryBuilder),
r#"SELECT "character" FROM "character" WHERE LOWER("character") LIKE LOWER("font_id")"#
);
}

#[test]
fn select_not_like_with_expr() {
assert_eq!(
Query::select()
.column(Char::Character)
.from(Char::Table)
.and_where(Expr::col(Char::Character).not_like(Expr::col(Char::FontId)))
.to_string(PostgresQueryBuilder),
r#"SELECT "character" FROM "character" WHERE "character" NOT LIKE "font_id""#
);
}

#[test]
fn select_ilike_with_expr() {
assert_eq!(
Query::select()
.column(Char::Character)
.from(Char::Table)
.and_where(Expr::col(Char::Character).ilike(Expr::col(Char::FontId)))
.to_string(PostgresQueryBuilder),
r#"SELECT "character" FROM "character" WHERE "character" ILIKE "font_id""#
);
}

#[test]
fn select_not_ilike_with_expr() {
assert_eq!(
Query::select()
.column(Char::Character)
.from(Char::Table)
.and_where(Expr::col(Char::Character).not_ilike(Expr::col(Char::FontId)))
.to_string(PostgresQueryBuilder),
r#"SELECT "character" FROM "character" WHERE "character" NOT ILIKE "font_id""#
);
}
39 changes: 39 additions & 0 deletions tests/sqlite/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1859,3 +1859,42 @@ fn recursive_with_multiple_ctes() {
r#"WITH RECURSIVE "sub1" ("a") AS (SELECT * FROM "character") , "sub2" ("b") AS (SELECT * FROM "character") SELECT * FROM "sub1" UNION ALL SELECT * FROM "sub2""#
);
}

#[test]
fn select_like_with_expr() {
assert_eq!(
Query::select()
.column(Char::Character)
.from(Char::Table)
.and_where(Expr::col(Char::Character).like(Expr::col(Char::FontId)))
.to_string(SqliteQueryBuilder),
r#"SELECT "character" FROM "character" WHERE "character" LIKE "font_id""#
);
}

#[test]
fn select_like_with_function() {
assert_eq!(
Query::select()
.column(Char::Character)
.from(Char::Table)
.and_where(
Expr::expr(Func::lower(Expr::col(Char::Character)))
.like(Func::lower(Expr::col(Char::FontId)))
)
.to_string(SqliteQueryBuilder),
r#"SELECT "character" FROM "character" WHERE LOWER("character") LIKE LOWER("font_id")"#
);
}

#[test]
fn select_not_like_with_expr() {
assert_eq!(
Query::select()
.column(Char::Character)
.from(Char::Table)
.and_where(Expr::col(Char::Character).not_like(Expr::col(Char::FontId)))
.to_string(SqliteQueryBuilder),
r#"SELECT "character" FROM "character" WHERE "character" NOT LIKE "font_id""#
);
}