use bitflags::bitflags;
use crate::compile::CompiledSql;
use crate::expr::{AggregateExpr, Expr, ExprNode, ExprOperand, IntoExpr, NumericExprType, TrimDirection, Value, VectorBinaryOp};
use crate::query::Select;
use crate::PgVector;
pub trait StringUnaryExpr {
type Output;
}
impl StringUnaryExpr for String {
type Output = String;
}
impl StringUnaryExpr for Option<String> {
type Output = Option<String>;
}
pub trait StringLengthExpr {
type Output;
}
impl StringLengthExpr for String {
type Output = i32;
}
impl StringLengthExpr for Option<String> {
type Output = Option<i32>;
}
pub trait StringSplitExpr {
type Output;
}
impl StringSplitExpr for String {
type Output = Vec<String>;
}
impl StringSplitExpr for Option<String> {
type Output = Option<Vec<String>>;
}
pub trait StringBinaryExpr<Rhs, Result> {
type Output;
}
impl<Result> StringBinaryExpr<String, Result> for String {
type Output = Result;
}
impl<Result> StringBinaryExpr<Option<String>, Result> for String {
type Output = Option<Result>;
}
impl<Result> StringBinaryExpr<String, Result> for Option<String> {
type Output = Option<Result>;
}
impl<Result> StringBinaryExpr<Option<String>, Result> for Option<String> {
type Output = Option<Result>;
}
#[doc(hidden)]
pub struct ConcatExpr {
node: ExprNode,
}
pub trait IntoConcatExpr {
fn into_concat_expr(self) -> ConcatExpr;
}
impl<T> IntoConcatExpr for T
where
T: ExprOperand,
T::Value: StringUnaryExpr,
{
fn into_concat_expr(self) -> ConcatExpr {
ConcatExpr {
node: self.into_operand_expr().node,
}
}
}
impl IntoConcatExpr for ConcatExpr {
fn into_concat_expr(self) -> ConcatExpr {
self
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct RegexReplaceFlags: u8 {
const CASE_INSENSITIVE = 1 << 0;
const GLOBAL = 1 << 1;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct RegexSplitFlags: u8 {
const CASE_INSENSITIVE = 1 << 0;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NormalizationForm {
Nfc,
Nfd,
Nfkc,
Nfkd,
}
pub trait StringBoolExpr {
type Output;
}
impl StringBoolExpr for String {
type Output = bool;
}
impl StringBoolExpr for Option<String> {
type Output = Option<bool>;
}
pub trait CodepointExpr {
type Output;
}
impl CodepointExpr for i32 {
type Output = String;
}
impl CodepointExpr for Option<i32> {
type Output = Option<String>;
}
impl RegexReplaceFlags {
fn as_postgres_str(self) -> &'static str {
match (self.contains(Self::GLOBAL), self.contains(Self::CASE_INSENSITIVE)) {
(false, false) => "",
(false, true) => "i",
(true, false) => "g",
(true, true) => "gi",
}
}
}
impl RegexSplitFlags {
fn as_postgres_str(self) -> &'static str {
if self.contains(Self::CASE_INSENSITIVE) {
"i"
} else {
""
}
}
}
fn unary_string_fn<T>(name: &'static str, arg: impl IntoExpr<T>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
let expr = arg.into_expr();
Expr::new(ExprNode::Func {
name,
args: vec![expr.node],
})
}
fn string_length_fn<T>(name: &'static str, arg: impl IntoExpr<T>) -> Expr<<T as StringLengthExpr>::Output>
where
T: StringLengthExpr,
{
let expr = arg.into_expr();
Expr::new(ExprNode::Func {
name,
args: vec![expr.node],
})
}
fn string_fn<T>(name: &'static str, arg: impl IntoExpr<T>, extra_args: Vec<ExprNode>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
let mut args = vec![arg.into_expr().node];
args.extend(extra_args);
Expr::new(ExprNode::Func { name, args })
}
fn binary_string_fn<L, R, O>(name: &'static str, left: impl IntoExpr<L>, right: impl IntoExpr<R>) -> Expr<O> {
let left = left.into_expr();
let right = right.into_expr();
Expr::new(ExprNode::Func {
name,
args: vec![left.node, right.node],
})
}
fn ternary_string_fn<A, B, C, O>(
name: &'static str,
first: impl IntoExpr<A>,
second: impl IntoExpr<B>,
third: impl IntoExpr<C>,
) -> Expr<O> {
Expr::new(ExprNode::Func {
name,
args: vec![first.into_expr().node, second.into_expr().node, third.into_expr().node],
})
}
fn string_expr_nodes<I, A>(args: I) -> Vec<ExprNode>
where
I: IntoIterator<Item = A>,
A: IntoConcatExpr,
{
args.into_iter().map(|arg| arg.into_concat_expr().node).collect()
}
fn directed_trim_fn<T>(
arg: impl IntoExpr<T>,
direction: TrimDirection,
characters: Option<Expr<String>>,
) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
Expr::new(ExprNode::Trim {
direction,
expr: Box::new(arg.into_expr().node),
characters: characters.map(|characters| Box::new(characters.node)),
})
}
pub fn upper<T>(arg: impl IntoExpr<T>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
unary_string_fn("UPPER", arg)
}
pub fn lower<T>(arg: impl IntoExpr<T>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
unary_string_fn("LOWER", arg)
}
pub fn title_case<T>(expression: impl IntoExpr<T>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
unary_string_fn("INITCAP", expression)
}
pub fn replace<S, F, T>(
expression: impl IntoExpr<S>,
from: impl IntoExpr<F>,
to: impl IntoExpr<T>,
) -> Expr<<<S as StringBinaryExpr<F, String>>::Output as StringBinaryExpr<T, String>>::Output>
where
S: StringBinaryExpr<F, String>,
<S as StringBinaryExpr<F, String>>::Output: StringBinaryExpr<T, String>,
{
ternary_string_fn("REPLACE", expression, from, to)
}
pub fn replace_range<S, R>(
expression: impl IntoExpr<S>,
replacement: impl IntoExpr<R>,
start: impl IntoExpr<i32>,
count: impl IntoExpr<i32>,
) -> Expr<<S as StringBinaryExpr<R, String>>::Output>
where
S: StringBinaryExpr<R, String>,
{
Expr::new(ExprNode::Func {
name: "OVERLAY",
args: vec![
expression.into_expr().node,
replacement.into_expr().node,
start.into_expr().node,
count.into_expr().node,
],
})
}
pub fn translate_chars<S, F, T>(
expression: impl IntoExpr<S>,
from: impl IntoExpr<F>,
to: impl IntoExpr<T>,
) -> Expr<<<S as StringBinaryExpr<F, String>>::Output as StringBinaryExpr<T, String>>::Output>
where
S: StringBinaryExpr<F, String>,
<S as StringBinaryExpr<F, String>>::Output: StringBinaryExpr<T, String>,
{
ternary_string_fn("TRANSLATE", expression, from, to)
}
pub fn reverse<T>(expression: impl IntoExpr<T>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
unary_string_fn("REVERSE", expression)
}
pub fn trim<T>(arg: impl IntoExpr<T>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
unary_string_fn("TRIM", arg)
}
pub fn trim_chars<T>(arg: impl IntoExpr<T>, characters: impl IntoExpr<String>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
directed_trim_fn(arg, TrimDirection::Both, Some(characters.into_expr()))
}
pub fn trim_start<T>(arg: impl IntoExpr<T>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
directed_trim_fn(arg, TrimDirection::Leading, None)
}
pub fn trim_start_chars<T>(arg: impl IntoExpr<T>, characters: impl IntoExpr<String>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
directed_trim_fn(arg, TrimDirection::Leading, Some(characters.into_expr()))
}
pub fn trim_end<T>(arg: impl IntoExpr<T>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
directed_trim_fn(arg, TrimDirection::Trailing, None)
}
pub fn trim_end_chars<T>(arg: impl IntoExpr<T>, characters: impl IntoExpr<String>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
directed_trim_fn(arg, TrimDirection::Trailing, Some(characters.into_expr()))
}
pub fn char_length<T>(arg: impl IntoExpr<T>) -> Expr<<T as StringLengthExpr>::Output>
where
T: StringLengthExpr,
{
string_length_fn("CHAR_LENGTH", arg)
}
pub fn byte_length<T>(arg: impl IntoExpr<T>) -> Expr<<T as StringLengthExpr>::Output>
where
T: StringLengthExpr,
{
string_length_fn("OCTET_LENGTH", arg)
}
pub fn bit_length<T>(arg: impl IntoExpr<T>) -> Expr<<T as StringLengthExpr>::Output>
where
T: StringLengthExpr,
{
string_length_fn("BIT_LENGTH", arg)
}
pub fn position<L, R>(expression: impl IntoExpr<L>, substring: impl IntoExpr<R>) -> Expr<<L as StringBinaryExpr<R, i32>>::Output>
where
L: StringBinaryExpr<R, i32>,
{
binary_string_fn("STRPOS", expression, substring)
}
pub fn starts_with<L, R>(expression: impl IntoExpr<L>, prefix: impl IntoExpr<R>) -> Expr<<L as StringBinaryExpr<R, bool>>::Output>
where
L: StringBinaryExpr<R, bool>,
{
binary_string_fn("STARTS_WITH", expression, prefix)
}
pub fn concat<I, A>(values: I) -> Expr<String>
where
I: IntoIterator<Item = A>,
A: IntoConcatExpr,
{
let args = string_expr_nodes(values);
Expr::new(ExprNode::Func { name: "CONCAT", args })
}
pub fn concat_with_separator<S, I, A>(separator: impl IntoExpr<S>, values: I) -> Expr<<S as StringUnaryExpr>::Output>
where
S: StringUnaryExpr,
I: IntoIterator<Item = A>,
A: IntoConcatExpr,
{
let mut args = vec![separator.into_expr().node];
args.extend(string_expr_nodes(values));
Expr::new(ExprNode::Func { name: "CONCAT_WS", args })
}
pub fn split<S, D>(expression: impl IntoExpr<S>, delimiter: impl IntoExpr<D>) -> Expr<<S as StringSplitExpr>::Output>
where
S: StringSplitExpr,
D: StringUnaryExpr,
{
binary_string_fn("STRING_TO_ARRAY", expression, delimiter)
}
pub fn split_part<S, D>(
expression: impl IntoExpr<S>,
delimiter: impl IntoExpr<D>,
index: impl IntoExpr<i32>,
) -> Expr<<S as StringBinaryExpr<D, String>>::Output>
where
S: StringBinaryExpr<D, String>,
{
let expression = expression.into_expr();
let delimiter = delimiter.into_expr();
let index = index.into_expr();
Expr::new(ExprNode::Func {
name: "SPLIT_PART",
args: vec![expression.node, delimiter.node, index.node],
})
}
pub fn regex_is_match<L, R>(expression: impl IntoExpr<L>, pattern: impl IntoExpr<R>) -> Expr<<L as StringBinaryExpr<R, bool>>::Output>
where
L: StringBinaryExpr<R, bool>,
{
binary_string_fn("REGEXP_LIKE", expression, pattern)
}
pub fn regex_count<L, R>(expression: impl IntoExpr<L>, pattern: impl IntoExpr<R>) -> Expr<<L as StringBinaryExpr<R, i32>>::Output>
where
L: StringBinaryExpr<R, i32>,
{
binary_string_fn("REGEXP_COUNT", expression, pattern)
}
pub fn regex_position<L, R>(expression: impl IntoExpr<L>, pattern: impl IntoExpr<R>) -> Expr<<L as StringBinaryExpr<R, i32>>::Output>
where
L: StringBinaryExpr<R, i32>,
{
binary_string_fn("REGEXP_INSTR", expression, pattern)
}
pub fn regex_captures<L, R>(expression: impl IntoExpr<L>, pattern: impl IntoExpr<R>) -> Expr<Option<Vec<Option<String>>>>
where
L: StringUnaryExpr,
R: StringUnaryExpr,
{
binary_string_fn("REGEXP_MATCH", expression, pattern)
}
pub fn regex_extract<L, R>(expression: impl IntoExpr<L>, pattern: impl IntoExpr<R>) -> Expr<Option<String>>
where
L: StringUnaryExpr,
R: StringUnaryExpr,
{
binary_string_fn("REGEXP_SUBSTR", expression, pattern)
}
pub fn left<T>(arg: impl IntoExpr<T>, count: impl IntoExpr<i32>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
string_fn("LEFT", arg, vec![count.into_expr().node])
}
pub fn right<T>(arg: impl IntoExpr<T>, count: impl IntoExpr<i32>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
string_fn("RIGHT", arg, vec![count.into_expr().node])
}
pub fn substring<T>(arg: impl IntoExpr<T>, start: impl IntoExpr<i32>, count: impl IntoExpr<i32>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
string_fn("SUBSTRING", arg, vec![start.into_expr().node, count.into_expr().node])
}
pub fn repeat<T>(arg: impl IntoExpr<T>, count: impl IntoExpr<i32>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
string_fn("REPEAT", arg, vec![count.into_expr().node])
}
pub fn pad_start<T>(arg: impl IntoExpr<T>, length: impl IntoExpr<i32>, fill: impl IntoExpr<String>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
string_fn("LPAD", arg, vec![length.into_expr().node, fill.into_expr().node])
}
pub fn pad_end<T>(arg: impl IntoExpr<T>, length: impl IntoExpr<i32>, fill: impl IntoExpr<String>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
string_fn("RPAD", arg, vec![length.into_expr().node, fill.into_expr().node])
}
pub fn regex_replace<S, P, R, SP, O>(
source: impl IntoExpr<S>,
pattern: impl IntoExpr<P>,
replacement: impl IntoExpr<R>,
flags: RegexReplaceFlags,
) -> Expr<O>
where
S: StringBinaryExpr<P, String, Output = SP>,
SP: StringBinaryExpr<R, String, Output = O>,
{
Expr::new(ExprNode::Func {
name: "REGEXP_REPLACE",
args: vec![
source.into_expr().node,
pattern.into_expr().node,
replacement.into_expr().node,
ExprNode::Value(Value::String(flags.as_postgres_str().to_string())),
],
})
}
pub fn regex_split<S, P, O>(source: impl IntoExpr<S>, pattern: impl IntoExpr<P>, flags: RegexSplitFlags) -> Expr<O>
where
S: StringBinaryExpr<P, Vec<String>, Output = O>,
{
Expr::new(ExprNode::Func {
name: "REGEXP_SPLIT_TO_ARRAY",
args: vec![
source.into_expr().node,
pattern.into_expr().node,
ExprNode::Value(Value::String(flags.as_postgres_str().to_string())),
],
})
}
pub fn normalize<T>(arg: impl IntoExpr<T>, form: NormalizationForm) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
Expr::new(ExprNode::Normalize {
expr: Box::new(arg.into_expr().node),
form,
})
}
pub fn first_codepoint<T>(arg: impl IntoExpr<T>) -> Expr<<T as StringLengthExpr>::Output>
where
T: StringLengthExpr,
{
string_length_fn("ASCII", arg)
}
pub fn from_codepoint<T>(arg: impl IntoExpr<T>) -> Expr<<T as CodepointExpr>::Output>
where
T: CodepointExpr,
{
let expr = arg.into_expr();
Expr::new(ExprNode::Func {
name: "CHR",
args: vec![expr.node],
})
}
pub fn to_ascii<T>(arg: impl IntoExpr<T>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
unary_string_fn("TO_ASCII", arg)
}
pub fn case_fold<T>(arg: impl IntoExpr<T>) -> Expr<<T as StringUnaryExpr>::Output>
where
T: StringUnaryExpr,
{
unary_string_fn("CASEFOLD", arg)
}
pub fn is_unicode_assigned<T>(arg: impl IntoExpr<T>) -> Expr<<T as StringBoolExpr>::Output>
where
T: StringBoolExpr,
{
let expr = arg.into_expr();
Expr::new(ExprNode::Func {
name: "UNICODE_ASSIGNED",
args: vec![expr.node],
})
}
pub fn count<T>(arg: impl IntoExpr<T>) -> AggregateExpr<i64> {
let expr = arg.into_expr();
Expr::new(ExprNode::Func {
name: "COUNT",
args: vec![expr.node],
})
}
pub fn sum<T>(arg: impl IntoExpr<T>) -> AggregateExpr<T> {
let expr = arg.into_expr();
Expr::new(ExprNode::Func {
name: "SUM",
args: vec![expr.node],
})
}
pub trait NullableAggregateOutput {
type Output;
}
macro_rules! impl_nullable_aggregate_output {
($($ty:ty),+ $(,)?) => {
$(
impl NullableAggregateOutput for $ty {
type Output = Option<$ty>;
}
impl NullableAggregateOutput for Option<$ty> {
type Output = Option<$ty>;
}
)+
};
}
impl_nullable_aggregate_output!(
String,
i16,
i32,
i64,
f32,
f64,
uuid::Uuid,
chrono::NaiveDateTime,
chrono::DateTime<chrono::Utc>,
chrono::NaiveDate,
chrono::NaiveTime,
crate::PgInterval,
);
pub fn min<T>(arg: impl IntoExpr<T>) -> AggregateExpr<<T as NullableAggregateOutput>::Output>
where
T: NullableAggregateOutput,
{
let expr = arg.into_expr();
Expr::new(ExprNode::Func {
name: "MIN",
args: vec![expr.node],
})
}
pub fn max<T>(arg: impl IntoExpr<T>) -> AggregateExpr<<T as NullableAggregateOutput>::Output>
where
T: NullableAggregateOutput,
{
let expr = arg.into_expr();
Expr::new(ExprNode::Func {
name: "MAX",
args: vec![expr.node],
})
}
pub fn coalesce<T>(a: impl IntoExpr<T>, b: impl IntoExpr<T>) -> Expr<T> {
let left = a.into_expr();
let right = b.into_expr();
Expr::new(ExprNode::Func {
name: "COALESCE",
args: vec![left.node, right.node],
})
}
pub fn least<T>(a: impl IntoExpr<T>, b: impl IntoExpr<T>) -> Expr<T> {
let left = a.into_expr();
let right = b.into_expr();
Expr::new(ExprNode::Func {
name: "LEAST",
args: vec![left.node, right.node],
})
}
pub fn greatest<T>(a: impl IntoExpr<T>, b: impl IntoExpr<T>) -> Expr<T> {
let left = a.into_expr();
let right = b.into_expr();
Expr::new(ExprNode::Func {
name: "GREATEST",
args: vec![left.node, right.node],
})
}
pub fn power<B, E>(base: impl IntoExpr<B>, exponent: impl IntoExpr<E>) -> Expr<f64>
where
B: NumericExprType,
E: NumericExprType,
{
let base = base.into_expr();
let exponent = exponent.into_expr();
Expr::new(ExprNode::Func {
name: "POWER",
args: vec![base.node, exponent.node],
})
}
pub fn date_trunc<T>(part: impl IntoExpr<String>, value: impl IntoExpr<T>) -> Expr<T> {
let part = part.into_expr();
let value = value.into_expr();
Expr::new(ExprNode::Func {
name: "DATE_TRUNC",
args: vec![part.node, value.node],
})
}
fn exists_expr(subquery: CompiledSql) -> Expr<bool> {
Expr::new(ExprNode::Exists { subquery })
}
pub fn exists<Out, Loads, Lock, DistinctState, GroupState>(subquery: Select<Out, Loads, Lock, DistinctState, GroupState>) -> Expr<bool> {
exists_expr(subquery.compile_for_exists())
}
pub trait VectorExpr<const N: usize> {}
impl<const N: usize> VectorExpr<N> for PgVector<N> {}
impl<const N: usize> VectorExpr<N> for Option<PgVector<N>> {}
fn vector_binary_fn<const N: usize, L, R>(name: &'static str, left: impl IntoExpr<L>, right: impl IntoExpr<R>) -> Expr<f32>
where
L: VectorExpr<N>,
R: VectorExpr<N>,
{
let left = left.into_expr();
let right = right.into_expr();
Expr::new(ExprNode::Func {
name,
args: vec![left.node, right.node],
})
}
fn vector_binary_operator<const N: usize, L, R>(op: VectorBinaryOp, left: impl IntoExpr<L>, right: impl IntoExpr<R>) -> Expr<f32>
where
L: VectorExpr<N>,
R: VectorExpr<N>,
{
let left = left.into_expr();
let right = right.into_expr();
Expr::new(ExprNode::VectorBinary {
left: Box::new(left.node),
op,
right: Box::new(right.node),
})
}
pub fn l2_distance<const N: usize, L, R>(left: impl IntoExpr<L>, right: impl IntoExpr<R>) -> Expr<f32>
where
L: VectorExpr<N>,
R: VectorExpr<N>,
{
vector_binary_operator::<N, L, R>(VectorBinaryOp::L2Distance, left, right)
}
pub fn cosine_distance<const N: usize, L, R>(left: impl IntoExpr<L>, right: impl IntoExpr<R>) -> Expr<f32>
where
L: VectorExpr<N>,
R: VectorExpr<N>,
{
vector_binary_operator::<N, L, R>(VectorBinaryOp::CosineDistance, left, right)
}
pub fn inner_product<const N: usize, L, R>(left: impl IntoExpr<L>, right: impl IntoExpr<R>) -> Expr<f32>
where
L: VectorExpr<N>,
R: VectorExpr<N>,
{
vector_binary_fn::<N, L, R>("INNER_PRODUCT", left, right)
}
pub fn l1_distance<const N: usize, L, R>(left: impl IntoExpr<L>, right: impl IntoExpr<R>) -> Expr<f32>
where
L: VectorExpr<N>,
R: VectorExpr<N>,
{
vector_binary_operator::<N, L, R>(VectorBinaryOp::L1Distance, left, right)
}
pub fn inner_product_distance<const N: usize, L, R>(left: impl IntoExpr<L>, right: impl IntoExpr<R>) -> Expr<f32>
where
L: VectorExpr<N>,
R: VectorExpr<N>,
{
vector_binary_operator::<N, L, R>(VectorBinaryOp::InnerProductDistance, left, right)
}