use crate::sql::{SQL, Token};
use crate::traits::{SQLParam, ToSQL};
use crate::types::{Bool, Compatible, Textual};
use super::{Expr, NonNull, SQLExpr, Scalar};
fn binary_op<'a, V, L, R>(left: L, operator: Token, right: R) -> SQL<'a, V>
where
V: SQLParam + 'a,
L: ToSQL<'a, V>,
R: ToSQL<'a, V>,
{
let right_sql = right.into_sql();
let right_sql = if right_sql.is_subquery() {
right_sql.parens()
} else {
right_sql
};
left.into_sql().push(operator).append(right_sql)
}
pub fn eq<'a, V, L, R>(left: L, right: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
V: SQLParam + 'a,
L: Expr<'a, V>,
R: Expr<'a, V>,
L::SQLType: Compatible<R::SQLType>,
{
SQLExpr::new(binary_op(left, Token::EQ, right))
}
pub fn neq<'a, V, L, R>(left: L, right: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
V: SQLParam + 'a,
L: Expr<'a, V>,
R: Expr<'a, V>,
L::SQLType: Compatible<R::SQLType>,
{
SQLExpr::new(binary_op(left, Token::NE, right))
}
pub fn gt<'a, V, L, R>(left: L, right: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
V: SQLParam + 'a,
L: Expr<'a, V>,
R: Expr<'a, V>,
L::SQLType: Compatible<R::SQLType>,
{
SQLExpr::new(binary_op(left, Token::GT, right))
}
pub fn gte<'a, V, L, R>(left: L, right: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
V: SQLParam + 'a,
L: Expr<'a, V>,
R: Expr<'a, V>,
L::SQLType: Compatible<R::SQLType>,
{
SQLExpr::new(binary_op(left, Token::GE, right))
}
pub fn lt<'a, V, L, R>(left: L, right: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
V: SQLParam + 'a,
L: Expr<'a, V>,
R: Expr<'a, V>,
L::SQLType: Compatible<R::SQLType>,
{
SQLExpr::new(binary_op(left, Token::LT, right))
}
pub fn lte<'a, V, L, R>(left: L, right: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
V: SQLParam + 'a,
L: Expr<'a, V>,
R: Expr<'a, V>,
L::SQLType: Compatible<R::SQLType>,
{
SQLExpr::new(binary_op(left, Token::LE, right))
}
pub fn like<'a, V, L, R>(left: L, pattern: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
V: SQLParam + 'a,
L: Expr<'a, V>,
R: Expr<'a, V>,
L::SQLType: Textual,
R::SQLType: Textual,
{
SQLExpr::new(left.into_sql().push(Token::LIKE).append(pattern.into_sql()))
}
pub fn not_like<'a, V, L, R>(left: L, pattern: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
V: SQLParam + 'a,
L: Expr<'a, V>,
R: Expr<'a, V>,
L::SQLType: Textual,
R::SQLType: Textual,
{
SQLExpr::new(
left.into_sql()
.push(Token::NOT)
.push(Token::LIKE)
.append(pattern.into_sql()),
)
}
pub fn between<'a, V, E, L, H>(expr: E, low: L, high: H) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
L: Expr<'a, V>,
H: Expr<'a, V>,
E::SQLType: Compatible<L::SQLType> + Compatible<H::SQLType>,
{
SQLExpr::new(
SQL::from(Token::LPAREN)
.append(expr.into_sql())
.push(Token::BETWEEN)
.append(low.into_sql())
.push(Token::AND)
.append(high.into_sql())
.push(Token::RPAREN),
)
}
pub fn not_between<'a, V, E, L, H>(
expr: E,
low: L,
high: H,
) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
L: Expr<'a, V>,
H: Expr<'a, V>,
E::SQLType: Compatible<L::SQLType> + Compatible<H::SQLType>,
{
SQLExpr::new(
SQL::from(Token::LPAREN)
.append(expr.into_sql())
.push(Token::NOT)
.push(Token::BETWEEN)
.append(low.into_sql())
.push(Token::AND)
.append(high.into_sql())
.push(Token::RPAREN),
)
}
pub fn is_null<'a, V, E>(expr: E) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
{
SQLExpr::new(expr.into_sql().push(Token::IS).push(Token::NULL))
}
pub fn is_not_null<'a, V, E>(expr: E) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
{
SQLExpr::new(
expr.into_sql()
.push(Token::IS)
.push(Token::NOT)
.push(Token::NULL),
)
}
pub trait ExprExt<'a, V: SQLParam>: Expr<'a, V> + Sized {
fn eq<R>(self, other: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
R: Expr<'a, V>,
Self::SQLType: Compatible<R::SQLType>,
{
eq(self, other)
}
fn ne<R>(self, other: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
R: Expr<'a, V>,
Self::SQLType: Compatible<R::SQLType>,
{
neq(self, other)
}
fn gt<R>(self, other: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
R: Expr<'a, V>,
Self::SQLType: Compatible<R::SQLType>,
{
gt(self, other)
}
fn ge<R>(self, other: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
R: Expr<'a, V>,
Self::SQLType: Compatible<R::SQLType>,
{
gte(self, other)
}
fn lt<R>(self, other: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
R: Expr<'a, V>,
Self::SQLType: Compatible<R::SQLType>,
{
lt(self, other)
}
fn le<R>(self, other: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
R: Expr<'a, V>,
Self::SQLType: Compatible<R::SQLType>,
{
lte(self, other)
}
fn like<R>(self, pattern: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
R: Expr<'a, V>,
Self::SQLType: Textual,
R::SQLType: Textual,
{
like(self, pattern)
}
fn not_like<R>(self, pattern: R) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
R: Expr<'a, V>,
Self::SQLType: Textual,
R::SQLType: Textual,
{
not_like(self, pattern)
}
#[allow(clippy::wrong_self_convention)]
fn is_null(self) -> SQLExpr<'a, V, Bool, NonNull, Scalar> {
is_null(self)
}
#[allow(clippy::wrong_self_convention)]
fn is_not_null(self) -> SQLExpr<'a, V, Bool, NonNull, Scalar> {
is_not_null(self)
}
fn between<L, H>(self, low: L, high: H) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
L: Expr<'a, V>,
H: Expr<'a, V>,
Self::SQLType: Compatible<L::SQLType> + Compatible<H::SQLType>,
{
between(self, low, high)
}
fn not_between<L, H>(self, low: L, high: H) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
L: Expr<'a, V>,
H: Expr<'a, V>,
Self::SQLType: Compatible<L::SQLType> + Compatible<H::SQLType>,
{
not_between(self, low, high)
}
fn in_array<I, R>(self, values: I) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
I: IntoIterator<Item = R>,
R: Expr<'a, V>,
Self::SQLType: Compatible<R::SQLType>,
{
crate::expr::in_array(self, values)
}
fn not_in_array<I, R>(self, values: I) -> SQLExpr<'a, V, Bool, NonNull, Scalar>
where
I: IntoIterator<Item = R>,
R: Expr<'a, V>,
Self::SQLType: Compatible<R::SQLType>,
{
crate::expr::not_in_array(self, values)
}
}
impl<'a, V: SQLParam, E: Expr<'a, V>> ExprExt<'a, V> for E {}