use crate::dialect::DialectTypes;
use crate::sql::{SQL, Token};
use crate::traits::{SQLParam, ToSQL};
use crate::types::{DataType, Integral, Textual};
use crate::{PostgresDialect, SQLiteDialect};
use drizzle_types::postgres::types::{
Char as PgChar, Int4 as PgInt4, Text as PgText, Varchar as PgVarchar,
};
use drizzle_types::sqlite::types::{Integer as SqliteInteger, Text as SqliteText};
use super::{AggOr, AggregateKind, Expr, NonNull, NullOr, Nullability, SQLExpr};
#[diagnostic::on_unimplemented(
message = "no length policy for `{Self}` on this dialect",
label = "length return type is not defined for this SQL type/dialect"
)]
pub trait LengthPolicy<D>: DataType {
type Output: DataType;
}
#[diagnostic::on_unimplemented(
message = "this string function is not available for this dialect",
label = "use a dialect-specific alternative"
)]
pub trait SQLiteStringSupport {}
#[diagnostic::on_unimplemented(
message = "this string function is not available for this dialect",
label = "use a dialect-specific alternative"
)]
pub trait PostgresStringSupport {}
impl LengthPolicy<SQLiteDialect> for SqliteText {
type Output = SqliteInteger;
}
impl LengthPolicy<SQLiteDialect> for drizzle_types::sqlite::types::Any {
type Output = SqliteInteger;
}
impl LengthPolicy<PostgresDialect> for PgVarchar {
type Output = PgInt4;
}
impl LengthPolicy<PostgresDialect> for PgText {
type Output = PgInt4;
}
impl LengthPolicy<PostgresDialect> for PgChar {
type Output = PgInt4;
}
impl SQLiteStringSupport for SQLiteDialect {}
impl PostgresStringSupport for PostgresDialect {}
pub fn upper<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Text, E::Nullable, E::Aggregate>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: Textual,
{
SQLExpr::new(SQL::func("UPPER", expr.into_sql()))
}
pub fn lower<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Text, E::Nullable, E::Aggregate>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: Textual,
{
SQLExpr::new(SQL::func("LOWER", expr.into_sql()))
}
pub fn trim<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Text, E::Nullable, E::Aggregate>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: Textual,
{
SQLExpr::new(SQL::func("TRIM", expr.into_sql()))
}
pub fn ltrim<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Text, E::Nullable, E::Aggregate>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: Textual,
{
SQLExpr::new(SQL::func("LTRIM", expr.into_sql()))
}
pub fn rtrim<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Text, E::Nullable, E::Aggregate>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: Textual,
{
SQLExpr::new(SQL::func("RTRIM", expr.into_sql()))
}
pub fn collate<'a, V, E>(
expr: E,
name: &'static str,
) -> SQLExpr<'a, V, E::SQLType, E::Nullable, E::Aggregate>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: Textual,
{
let inner = expr.into_sql().parens_if_subquery();
SQLExpr::new(
SQL::raw("(")
.append(inner)
.append(SQL::raw(format!(" COLLATE \"{name}\")"))),
)
}
#[allow(clippy::type_complexity)]
pub fn length<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <E::SQLType as LengthPolicy<V::DialectMarker>>::Output, E::Nullable, E::Aggregate>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: LengthPolicy<V::DialectMarker>,
{
SQLExpr::new(SQL::func("LENGTH", expr.into_sql()))
}
#[allow(clippy::type_complexity)]
pub fn substr<'a, V, E, S, L>(
expr: E,
start: S,
len: L,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Text,
E::Nullable,
<<E::Aggregate as AggOr<S::Aggregate>>::Output as AggOr<L::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: Textual,
S: Expr<'a, V>,
S::SQLType: Integral,
S::Aggregate: AggregateKind,
L: Expr<'a, V>,
L::SQLType: Integral,
L::Aggregate: AggregateKind,
E::Aggregate: AggOr<S::Aggregate>,
<E::Aggregate as AggOr<S::Aggregate>>::Output: AggOr<L::Aggregate>,
{
SQLExpr::new(SQL::func(
"SUBSTR",
expr.into_sql()
.push(Token::COMMA)
.append(start.into_sql())
.push(Token::COMMA)
.append(len.into_sql()),
))
}
#[allow(clippy::type_complexity)]
pub fn replace<'a, V, E, F, T>(
expr: E,
from: F,
to: T,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Text,
E::Nullable,
<<E::Aggregate as AggOr<F::Aggregate>>::Output as AggOr<T::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: Textual,
F: Expr<'a, V>,
F::SQLType: Textual,
F::Aggregate: AggregateKind,
T: Expr<'a, V>,
T::SQLType: Textual,
T::Aggregate: AggregateKind,
E::Aggregate: AggOr<F::Aggregate>,
<E::Aggregate as AggOr<F::Aggregate>>::Output: AggOr<T::Aggregate>,
{
SQLExpr::new(SQL::func(
"REPLACE",
expr.into_sql()
.push(Token::COMMA)
.append(from.into_sql())
.push(Token::COMMA)
.append(to.into_sql()),
))
}
#[allow(clippy::type_complexity)]
pub fn instr<'a, V, E, S>(
expr: E,
search: S,
) -> SQLExpr<
'a,
V,
drizzle_types::sqlite::types::Integer,
E::Nullable,
<E::Aggregate as AggOr<S::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: SQLiteStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
S: Expr<'a, V>,
S::SQLType: Textual,
S::Aggregate: AggregateKind,
E::Aggregate: AggOr<S::Aggregate>,
{
SQLExpr::new(SQL::func(
"INSTR",
expr.into_sql().push(Token::COMMA).append(search.into_sql()),
))
}
#[allow(clippy::type_complexity)]
pub fn strpos<'a, V, E, S>(
expr: E,
search: S,
) -> SQLExpr<
'a,
V,
drizzle_types::postgres::types::Int4,
E::Nullable,
<E::Aggregate as AggOr<S::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
S: Expr<'a, V>,
S::SQLType: Textual,
S::Aggregate: AggregateKind,
E::Aggregate: AggOr<S::Aggregate>,
{
SQLExpr::new(SQL::func(
"STRPOS",
expr.into_sql().push(Token::COMMA).append(search.into_sql()),
))
}
#[allow(clippy::type_complexity)]
pub fn concat<'a, V, E1, E2>(
expr1: E1,
expr2: E2,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Text,
<E1::Nullable as NullOr<E2::Nullable>>::Output,
<E1::Aggregate as AggOr<E2::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
E1: Expr<'a, V>,
E1::SQLType: Textual,
E2: Expr<'a, V>,
E2::SQLType: Textual,
E1::Nullable: NullOr<E2::Nullable>,
E2::Nullable: Nullability,
E2::Aggregate: AggregateKind,
E1::Aggregate: AggOr<E2::Aggregate>,
{
SQLExpr::new(
expr1
.into_sql()
.push(Token::CONCAT)
.append(expr2.into_sql()),
)
}
#[allow(clippy::type_complexity)]
pub fn concat_ws<'a, V, S, I>(
sep: S,
values: I,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Text,
S::Nullable,
<S::Aggregate as AggOr<<I::Item as Expr<'a, V>>::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
S: Expr<'a, V>,
S::SQLType: Textual,
I: IntoIterator,
I::Item: Expr<'a, V>,
<I::Item as Expr<'a, V>>::SQLType: Textual,
S::Aggregate: AggOr<<I::Item as Expr<'a, V>>::Aggregate>,
<I::Item as Expr<'a, V>>::Aggregate: AggregateKind,
{
let mut sql = sep.into_sql();
for value in values {
sql = sql.push(Token::COMMA).append(value.into_sql());
}
SQLExpr::new(SQL::func("CONCAT_WS", sql))
}
#[allow(clippy::type_complexity)]
pub fn left<'a, V, E, N>(
expr: E,
n: N,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Text,
E::Nullable,
<E::Aggregate as AggOr<N::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
N: Expr<'a, V>,
N::SQLType: Integral,
N::Aggregate: AggregateKind,
E::Aggregate: AggOr<N::Aggregate>,
{
SQLExpr::new(SQL::func(
"LEFT",
expr.into_sql().push(Token::COMMA).append(n.into_sql()),
))
}
#[allow(clippy::type_complexity)]
pub fn right<'a, V, E, N>(
expr: E,
n: N,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Text,
E::Nullable,
<E::Aggregate as AggOr<N::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
N: Expr<'a, V>,
N::SQLType: Integral,
N::Aggregate: AggregateKind,
E::Aggregate: AggOr<N::Aggregate>,
{
SQLExpr::new(SQL::func(
"RIGHT",
expr.into_sql().push(Token::COMMA).append(n.into_sql()),
))
}
#[allow(clippy::type_complexity)]
pub fn split_part<'a, V, E, D, N>(
expr: E,
delimiter: D,
n: N,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Text,
E::Nullable,
<<E::Aggregate as AggOr<D::Aggregate>>::Output as AggOr<N::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
D: Expr<'a, V>,
D::SQLType: Textual,
D::Aggregate: AggregateKind,
N: Expr<'a, V>,
N::SQLType: Integral,
N::Aggregate: AggregateKind,
E::Aggregate: AggOr<D::Aggregate>,
<E::Aggregate as AggOr<D::Aggregate>>::Output: AggOr<N::Aggregate>,
{
SQLExpr::new(SQL::func(
"SPLIT_PART",
expr.into_sql()
.push(Token::COMMA)
.append(delimiter.into_sql())
.push(Token::COMMA)
.append(n.into_sql()),
))
}
#[allow(clippy::type_complexity)]
pub fn lpad<'a, V, E, L, F>(
expr: E,
length: L,
fill: F,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Text,
E::Nullable,
<<E::Aggregate as AggOr<L::Aggregate>>::Output as AggOr<F::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
L: Expr<'a, V>,
L::SQLType: Integral,
L::Aggregate: AggregateKind,
F: Expr<'a, V>,
F::SQLType: Textual,
F::Aggregate: AggregateKind,
E::Aggregate: AggOr<L::Aggregate>,
<E::Aggregate as AggOr<L::Aggregate>>::Output: AggOr<F::Aggregate>,
{
SQLExpr::new(SQL::func(
"LPAD",
expr.into_sql()
.push(Token::COMMA)
.append(length.into_sql())
.push(Token::COMMA)
.append(fill.into_sql()),
))
}
#[allow(clippy::type_complexity)]
pub fn rpad<'a, V, E, L, F>(
expr: E,
length: L,
fill: F,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Text,
E::Nullable,
<<E::Aggregate as AggOr<L::Aggregate>>::Output as AggOr<F::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
L: Expr<'a, V>,
L::SQLType: Integral,
L::Aggregate: AggregateKind,
F: Expr<'a, V>,
F::SQLType: Textual,
F::Aggregate: AggregateKind,
E::Aggregate: AggOr<L::Aggregate>,
<E::Aggregate as AggOr<L::Aggregate>>::Output: AggOr<F::Aggregate>,
{
SQLExpr::new(SQL::func(
"RPAD",
expr.into_sql()
.push(Token::COMMA)
.append(length.into_sql())
.push(Token::COMMA)
.append(fill.into_sql()),
))
}
pub fn initcap<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Text, E::Nullable, E::Aggregate>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
{
SQLExpr::new(SQL::func("INITCAP", expr.into_sql()))
}
pub fn reverse<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Text, E::Nullable, E::Aggregate>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
{
SQLExpr::new(SQL::func("REVERSE", expr.into_sql()))
}
#[allow(clippy::type_complexity)]
pub fn repeat<'a, V, E, N>(
expr: E,
n: N,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Text,
E::Nullable,
<E::Aggregate as AggOr<N::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
N: Expr<'a, V>,
N::SQLType: Integral,
N::Aggregate: AggregateKind,
E::Aggregate: AggOr<N::Aggregate>,
{
SQLExpr::new(SQL::func(
"REPEAT",
expr.into_sql().push(Token::COMMA).append(n.into_sql()),
))
}
#[allow(clippy::type_complexity)]
pub fn starts_with<'a, V, E, P>(
expr: E,
prefix: P,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Bool,
NonNull,
<E::Aggregate as AggOr<P::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
P: Expr<'a, V>,
P::SQLType: Textual,
P::Aggregate: AggregateKind,
E::Aggregate: AggOr<P::Aggregate>,
{
SQLExpr::new(SQL::func(
"STARTS_WITH",
expr.into_sql().push(Token::COMMA).append(prefix.into_sql()),
))
}
pub trait CharLengthPolicy {
const CHAR_LENGTH_FN: &'static str;
}
impl CharLengthPolicy for SQLiteDialect {
const CHAR_LENGTH_FN: &'static str = "LENGTH";
}
impl CharLengthPolicy for PostgresDialect {
const CHAR_LENGTH_FN: &'static str = "CHAR_LENGTH";
}
#[allow(clippy::type_complexity)]
pub fn char_length<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <E::SQLType as LengthPolicy<V::DialectMarker>>::Output, E::Nullable, E::Aggregate>
where
V: SQLParam + 'a,
V::DialectMarker: CharLengthPolicy,
E: Expr<'a, V>,
E::SQLType: LengthPolicy<V::DialectMarker>,
{
SQLExpr::new(SQL::func(
<V::DialectMarker as CharLengthPolicy>::CHAR_LENGTH_FN,
expr.into_sql(),
))
}
#[allow(clippy::type_complexity)]
pub fn octet_length<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <E::SQLType as LengthPolicy<V::DialectMarker>>::Output, E::Nullable, E::Aggregate>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
E::SQLType: LengthPolicy<V::DialectMarker>,
{
SQLExpr::new(SQL::func("OCTET_LENGTH", expr.into_sql()))
}
#[allow(clippy::type_complexity)]
pub fn translate<'a, V, E, F, T>(
expr: E,
from: F,
to: T,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Text,
E::Nullable,
<<E::Aggregate as AggOr<F::Aggregate>>::Output as AggOr<T::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
F: Expr<'a, V>,
F::SQLType: Textual,
F::Aggregate: AggregateKind,
T: Expr<'a, V>,
T::SQLType: Textual,
T::Aggregate: AggregateKind,
E::Aggregate: AggOr<F::Aggregate>,
<E::Aggregate as AggOr<F::Aggregate>>::Output: AggOr<T::Aggregate>,
{
SQLExpr::new(SQL::func(
"TRANSLATE",
expr.into_sql()
.push(Token::COMMA)
.append(from.into_sql())
.push(Token::COMMA)
.append(to.into_sql()),
))
}
#[allow(clippy::type_complexity)]
pub fn regexp_replace<'a, V, E, P, R>(
expr: E,
pattern: P,
replacement: R,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Text,
E::Nullable,
<<E::Aggregate as AggOr<P::Aggregate>>::Output as AggOr<R::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
P: Expr<'a, V>,
P::SQLType: Textual,
P::Aggregate: AggregateKind,
R: Expr<'a, V>,
R::SQLType: Textual,
R::Aggregate: AggregateKind,
E::Aggregate: AggOr<P::Aggregate>,
<E::Aggregate as AggOr<P::Aggregate>>::Output: AggOr<R::Aggregate>,
{
SQLExpr::new(SQL::func(
"REGEXP_REPLACE",
expr.into_sql()
.push(Token::COMMA)
.append(pattern.into_sql())
.push(Token::COMMA)
.append(replacement.into_sql()),
))
}
#[allow(clippy::type_complexity)]
pub fn regexp_replace_flags<'a, V, E, P, R, F>(
expr: E,
pattern: P,
replacement: R,
flags: F,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as DialectTypes>::Text,
E::Nullable,
<<<E::Aggregate as AggOr<P::Aggregate>>::Output as AggOr<R::Aggregate>>::Output as AggOr<
F::Aggregate,
>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
P: Expr<'a, V>,
P::SQLType: Textual,
P::Aggregate: AggregateKind,
R: Expr<'a, V>,
R::SQLType: Textual,
R::Aggregate: AggregateKind,
F: Expr<'a, V>,
F::SQLType: Textual,
F::Aggregate: AggregateKind,
E::Aggregate: AggOr<P::Aggregate>,
<E::Aggregate as AggOr<P::Aggregate>>::Output: AggOr<R::Aggregate>,
<<E::Aggregate as AggOr<P::Aggregate>>::Output as AggOr<R::Aggregate>>::Output:
AggOr<F::Aggregate>,
{
SQLExpr::new(SQL::func(
"REGEXP_REPLACE",
expr.into_sql()
.push(Token::COMMA)
.append(pattern.into_sql())
.push(Token::COMMA)
.append(replacement.into_sql())
.push(Token::COMMA)
.append(flags.into_sql()),
))
}
#[allow(clippy::type_complexity)]
pub fn regexp_match<'a, V, E, P>(
expr: E,
pattern: P,
) -> SQLExpr<
'a,
V,
crate::types::Array<<V::DialectMarker as DialectTypes>::Text>,
super::Null,
<E::Aggregate as AggOr<P::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
P: Expr<'a, V>,
P::SQLType: Textual,
P::Aggregate: AggregateKind,
E::Aggregate: AggOr<P::Aggregate>,
{
SQLExpr::new(SQL::func(
"REGEXP_MATCH",
expr.into_sql()
.push(Token::COMMA)
.append(pattern.into_sql()),
))
}
#[allow(clippy::type_complexity)]
pub fn regexp_match_flags<'a, V, E, P, F>(
expr: E,
pattern: P,
flags: F,
) -> SQLExpr<
'a,
V,
crate::types::Array<<V::DialectMarker as DialectTypes>::Text>,
super::Null,
<<E::Aggregate as AggOr<P::Aggregate>>::Output as AggOr<F::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
V::DialectMarker: PostgresStringSupport,
E: Expr<'a, V>,
E::SQLType: Textual,
P: Expr<'a, V>,
P::SQLType: Textual,
P::Aggregate: AggregateKind,
F: Expr<'a, V>,
F::SQLType: Textual,
F::Aggregate: AggregateKind,
E::Aggregate: AggOr<P::Aggregate>,
<E::Aggregate as AggOr<P::Aggregate>>::Output: AggOr<F::Aggregate>,
{
SQLExpr::new(SQL::func(
"REGEXP_MATCH",
expr.into_sql()
.push(Token::COMMA)
.append(pattern.into_sql())
.push(Token::COMMA)
.append(flags.into_sql()),
))
}