use super::expr::{Expr, ScalarFn};
#[must_use]
pub fn lower(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::Lower, arg)
}
#[must_use]
pub fn upper(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::Upper, arg)
}
#[must_use]
pub fn length(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::Length, arg)
}
#[must_use]
pub fn trim(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::Trim, arg)
}
#[must_use]
pub fn ltrim(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::LTrim, arg)
}
#[must_use]
pub fn rtrim(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::RTrim, arg)
}
#[must_use]
pub fn abs(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::Abs, arg)
}
#[must_use]
pub fn ceil(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::Ceil, arg)
}
#[must_use]
pub fn floor(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::Floor, arg)
}
#[must_use]
pub fn round(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::Round, arg)
}
#[must_use]
pub fn round_to(arg: impl Into<Expr>, n: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::Round,
args: vec![arg.into(), n.into()],
}
}
#[must_use]
pub fn substr(s: impl Into<Expr>, start: impl Into<Expr>, length: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::Substr,
args: vec![s.into(), start.into(), length.into()],
}
}
#[must_use]
pub fn replace(s: impl Into<Expr>, from: impl Into<Expr>, to: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::Replace,
args: vec![s.into(), from.into(), to.into()],
}
}
#[must_use]
pub fn nullif(a: impl Into<Expr>, b: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::NullIf,
args: vec![a.into(), b.into()],
}
}
#[must_use]
pub fn concat<I>(args: I) -> Expr
where
I: IntoIterator<Item = Expr>,
{
variadic(ScalarFn::Concat, args)
}
#[must_use]
pub fn coalesce<I>(args: I) -> Expr
where
I: IntoIterator<Item = Expr>,
{
variadic(ScalarFn::Coalesce, args)
}
#[must_use]
pub fn greatest<I>(args: I) -> Expr
where
I: IntoIterator<Item = Expr>,
{
variadic(ScalarFn::Greatest, args)
}
#[must_use]
pub fn least<I>(args: I) -> Expr
where
I: IntoIterator<Item = Expr>,
{
variadic(ScalarFn::Least, args)
}
#[must_use]
pub fn now() -> Expr {
Expr::Function {
kind: ScalarFn::Now,
args: Vec::new(),
}
}
#[must_use]
pub fn extract_year(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::ExtractYear, arg)
}
#[must_use]
pub fn extract_month(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::ExtractMonth, arg)
}
#[must_use]
pub fn extract_day(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::ExtractDay, arg)
}
#[must_use]
pub fn extract_hour(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::ExtractHour, arg)
}
#[must_use]
pub fn extract_minute(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::ExtractMinute, arg)
}
#[must_use]
pub fn extract_second(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::ExtractSecond, arg)
}
#[must_use]
pub fn extract_week(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::ExtractWeek, arg)
}
#[must_use]
pub fn extract_weekday(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::ExtractWeekDay, arg)
}
#[must_use]
pub fn extract_quarter(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::ExtractQuarter, arg)
}
#[must_use]
pub fn trunc_date(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::TruncDate, arg)
}
#[must_use]
pub fn trunc_year(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::TruncYear, arg)
}
#[must_use]
pub fn trunc_month(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::TruncMonth, arg)
}
#[must_use]
pub fn trunc_day(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::TruncDay, arg)
}
fn unary(kind: ScalarFn, arg: impl Into<Expr>) -> Expr {
Expr::Function {
kind,
args: vec![arg.into()],
}
}
fn variadic<I>(kind: ScalarFn, args: I) -> Expr
where
I: IntoIterator<Item = Expr>,
{
Expr::Function {
kind,
args: args.into_iter().collect(),
}
}
#[must_use]
pub fn trigram_similarity(a: impl Into<Expr>, b: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::TrigramSimilarity,
args: vec![a.into(), b.into()],
}
}
#[must_use]
pub fn trigram_word_similarity(a: impl Into<Expr>, b: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::TrigramWordSimilarity,
args: vec![a.into(), b.into()],
}
}
#[must_use]
pub fn to_tsvector(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::ToTsVector, arg)
}
#[must_use]
pub fn plainto_tsquery(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::PlainToTsQuery, arg)
}
#[must_use]
pub fn ts_rank(vector: impl Into<Expr>, query: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::TsRank,
args: vec![vector.into(), query.into()],
}
}
#[must_use]
pub fn ts_headline(doc: impl Into<Expr>, query: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::TsHeadline,
args: vec![doc.into(), query.into()],
}
}
#[must_use]
pub fn ts_headline_with(
doc: impl Into<Expr>,
query: impl Into<Expr>,
options: impl Into<Expr>,
) -> Expr {
Expr::Function {
kind: ScalarFn::TsHeadline,
args: vec![doc.into(), query.into(), options.into()],
}
}
#[must_use]
pub fn phraseto_tsquery(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::PhraseToTsQuery, arg)
}
#[must_use]
pub fn websearch_to_tsquery(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::WebsearchToTsQuery, arg)
}
#[must_use]
pub fn to_tsquery(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::ToTsQuery, arg)
}
#[must_use]
pub fn ts_rank_cd(vector: impl Into<Expr>, query: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::TsRankCd,
args: vec![vector.into(), query.into()],
}
}
#[must_use]
pub fn cast(expr: impl Into<Expr>, ty: super::field_type::FieldType) -> Expr {
Expr::Cast {
expr: Box::new(expr.into()),
ty,
}
}
#[must_use]
pub fn lpad(s: impl Into<Expr>, len: impl Into<Expr>, fill: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::LPad,
args: vec![s.into(), len.into(), fill.into()],
}
}
#[must_use]
pub fn rpad(s: impl Into<Expr>, len: impl Into<Expr>, fill: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::RPad,
args: vec![s.into(), len.into(), fill.into()],
}
}
#[must_use]
pub fn md5(s: impl Into<Expr>) -> Expr {
unary(ScalarFn::Md5, s)
}
#[must_use]
pub fn sha1(s: impl Into<Expr>) -> Expr {
unary(ScalarFn::Sha1, s)
}
#[must_use]
pub fn sha256(s: impl Into<Expr>) -> Expr {
unary(ScalarFn::Sha256, s)
}
#[must_use]
pub fn position(needle: impl Into<Expr>, hay: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::Position,
args: vec![needle.into(), hay.into()],
}
}
#[must_use]
pub fn repeat(s: impl Into<Expr>, n: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::Repeat,
args: vec![s.into(), n.into()],
}
}
#[must_use]
pub fn reverse(s: impl Into<Expr>) -> Expr {
unary(ScalarFn::Reverse, s)
}
#[must_use]
pub fn sign(x: impl Into<Expr>) -> Expr {
unary(ScalarFn::Sign, x)
}
#[must_use]
pub fn mod_(a: impl Into<Expr>, b: impl Into<Expr>) -> Expr {
a.into().binop(super::expr::BinOp::Mod, b)
}
#[must_use]
pub fn power(a: impl Into<Expr>, b: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::Power,
args: vec![a.into(), b.into()],
}
}
#[must_use]
pub fn sqrt(x: impl Into<Expr>) -> Expr {
unary(ScalarFn::Sqrt, x)
}
#[must_use]
pub fn log(x: impl Into<Expr>) -> Expr {
unary(ScalarFn::Log, x)
}
#[must_use]
pub fn log_with_base(base: impl Into<Expr>, x: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::LogWithBase,
args: vec![base.into(), x.into()],
}
}
#[must_use]
pub fn exp(x: impl Into<Expr>) -> Expr {
unary(ScalarFn::Exp, x)
}
#[must_use]
pub fn pi() -> Expr {
Expr::Function {
kind: ScalarFn::Pi,
args: Vec::new(),
}
}
#[must_use]
pub fn random() -> Expr {
Expr::Function {
kind: ScalarFn::Random,
args: Vec::new(),
}
}
#[must_use]
pub fn make_interval(
years: impl Into<Expr>,
months: impl Into<Expr>,
days: impl Into<Expr>,
hours: impl Into<Expr>,
minutes: impl Into<Expr>,
seconds: impl Into<Expr>,
) -> Expr {
Expr::Function {
kind: ScalarFn::MakeInterval,
args: vec![
years.into(),
months.into(),
days.into(),
hours.into(),
minutes.into(),
seconds.into(),
],
}
}
#[must_use]
pub fn age(ts1: impl Into<Expr>, ts2: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::Age,
args: vec![ts1.into(), ts2.into()],
}
}
#[must_use]
pub fn trunc_with_tz(ts: impl Into<Expr>, unit: &'static str, tz: &'static str) -> Expr {
use super::SqlValue;
Expr::Function {
kind: ScalarFn::TruncWithTz,
args: vec![
ts.into(),
Expr::Literal(SqlValue::String(unit.to_owned())),
Expr::Literal(SqlValue::String(tz.to_owned())),
],
}
}
#[must_use]
pub fn json_path(source: impl Into<Expr>, keys: &[&str], as_text: bool) -> Expr {
use super::JsonPathStep;
Expr::JsonPath {
source: Box::new(source.into()),
path: keys
.iter()
.map(|k| JsonPathStep::Key((*k).to_owned()))
.collect(),
as_text,
}
}
#[must_use]
pub fn json_array_length(arg: impl Into<Expr>) -> Expr {
unary(ScalarFn::JsonArrayLength, arg)
}
#[must_use]
pub fn json_path_indexed(
source: impl Into<Expr>,
steps: impl IntoIterator<Item = super::JsonPathStep>,
as_text: bool,
) -> Expr {
Expr::JsonPath {
source: Box::new(source.into()),
path: steps.into_iter().collect(),
as_text,
}
}
#[must_use]
pub fn st_distance(a: impl Into<Expr>, b: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::StDistance,
args: vec![a.into(), b.into()],
}
}
#[must_use]
pub fn st_dwithin(a: impl Into<Expr>, b: impl Into<Expr>, distance: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::StDWithin,
args: vec![a.into(), b.into(), distance.into()],
}
}
#[must_use]
pub fn st_contains(a: impl Into<Expr>, b: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::StContains,
args: vec![a.into(), b.into()],
}
}
#[must_use]
pub fn st_within(a: impl Into<Expr>, b: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::StWithin,
args: vec![a.into(), b.into()],
}
}
#[must_use]
pub fn st_intersects(a: impl Into<Expr>, b: impl Into<Expr>) -> Expr {
Expr::Function {
kind: ScalarFn::StIntersects,
args: vec![a.into(), b.into()],
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::{SqlValue, F};
#[test]
fn unary_builds_function_with_one_arg() {
let e = lower(F("name"));
let Expr::Function { kind, args } = e else {
panic!("expected Function variant")
};
assert_eq!(kind, ScalarFn::Lower);
assert_eq!(args, vec![Expr::Column("name")]);
}
#[test]
fn variadic_collects_iter() {
let e = concat([F("a").into(), " ".into(), F("b").into()]);
let Expr::Function { kind, args } = e else {
panic!()
};
assert_eq!(kind, ScalarFn::Concat);
assert_eq!(args.len(), 3);
assert_eq!(args[0], Expr::Column("a"));
assert_eq!(args[1], Expr::Literal(SqlValue::String(" ".into())));
assert_eq!(args[2], Expr::Column("b"));
}
#[test]
fn coalesce_variadic_takes_vec() {
let args: Vec<Expr> = vec![F("a").into(), F("b").into(), 0_i32.into()];
let e = coalesce(args);
let Expr::Function { kind, args } = e else {
panic!()
};
assert_eq!(kind, ScalarFn::Coalesce);
assert_eq!(args.len(), 3);
}
#[test]
fn substr_is_3ary() {
let e = substr(F("title"), 1_i64, 10_i64);
let Expr::Function { kind, args } = e else {
panic!()
};
assert_eq!(kind, ScalarFn::Substr);
assert_eq!(args.len(), 3);
}
#[test]
fn round_one_arg_vs_two() {
let e = round(F("score"));
let Expr::Function { args, .. } = e else {
panic!()
};
assert_eq!(args.len(), 1);
let e = round_to(F("score"), 2_i32);
let Expr::Function { args, .. } = e else {
panic!()
};
assert_eq!(args.len(), 2);
}
#[test]
fn functions_compose_via_into_expr() {
let e = upper(concat([F("first").into(), " ".into(), F("last").into()]));
let Expr::Function {
kind: outer_kind,
args: outer_args,
} = e
else {
panic!()
};
assert_eq!(outer_kind, ScalarFn::Upper);
assert_eq!(outer_args.len(), 1);
let Expr::Function {
kind: inner_kind, ..
} = &outer_args[0]
else {
panic!("inner should be a Function")
};
assert_eq!(*inner_kind, ScalarFn::Concat);
}
}