use crate::logical_plan::Context;
use crate::prelude::*;
use crate::utils::{has_expr, output_name};
use polars_core::prelude::*;
#[cfg(feature = "temporal")]
use polars_core::utils::chrono::{NaiveDate, NaiveDateTime};
use std::fmt::{Debug, Formatter};
use std::ops::{BitAnd, BitOr, Deref};
use std::{
fmt,
ops::{Add, Div, Mul, Rem, Sub},
sync::Arc,
};
pub use crate::frame::IntoLazy;
pub trait SeriesUdf: Send + Sync {
fn call_udf(&self, s: Series) -> Result<Series>;
}
impl<F> SeriesUdf for F
where
F: Fn(Series) -> Result<Series> + Send + Sync,
{
fn call_udf(&self, s: Series) -> Result<Series> {
self(s)
}
}
impl Debug for dyn SeriesUdf {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "SeriesUdf")
}
}
pub trait SeriesBinaryUdf: Send + Sync {
fn call_udf(&self, a: Series, b: Series) -> Result<Series>;
}
impl<F> SeriesBinaryUdf for F
where
F: Fn(Series, Series) -> Result<Series> + Send + Sync,
{
fn call_udf(&self, a: Series, b: Series) -> Result<Series> {
self(a, b)
}
}
impl Debug for dyn SeriesBinaryUdf {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "SeriesBinaryUdf")
}
}
#[derive(Clone)]
pub struct NoEq<T>(T);
impl<T> NoEq<T> {
pub fn new(val: T) -> Self {
NoEq(val)
}
}
impl<T> PartialEq for NoEq<T> {
fn eq(&self, _other: &Self) -> bool {
false
}
}
impl<T> Debug for NoEq<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "no_eq")
}
}
impl<T> Deref for NoEq<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub trait BinaryUdfOutputField: Send + Sync {
fn get_field(
&self,
_input_schema: &Schema,
cntxt: Context,
field_a: &Field,
field_b: &Field,
) -> Option<Field>;
}
impl<F> BinaryUdfOutputField for F
where
F: Fn(&Schema, Context, &Field, &Field) -> Option<Field> + Send + Sync,
{
fn get_field(
&self,
input_schema: &Schema,
cntxt: Context,
field_a: &Field,
field_b: &Field,
) -> Option<Field> {
self(input_schema, cntxt, field_a, field_b)
}
}
#[derive(PartialEq, Clone)]
pub enum AggExpr {
Min(Box<Expr>),
Max(Box<Expr>),
Median(Box<Expr>),
NUnique(Box<Expr>),
First(Box<Expr>),
Last(Box<Expr>),
Mean(Box<Expr>),
List(Box<Expr>),
Count(Box<Expr>),
Quantile { expr: Box<Expr>, quantile: f64 },
Sum(Box<Expr>),
AggGroups(Box<Expr>),
Std(Box<Expr>),
Var(Box<Expr>),
}
impl AsRef<Expr> for AggExpr {
fn as_ref(&self) -> &Expr {
use AggExpr::*;
match self {
Min(e) => e,
Max(e) => e,
Median(e) => e,
NUnique(e) => e,
First(e) => e,
Last(e) => e,
Mean(e) => e,
List(e) => e,
Count(e) => e,
Quantile { expr, .. } => expr,
Sum(e) => e,
AggGroups(e) => e,
Std(e) => e,
Var(e) => e,
}
}
}
impl From<AggExpr> for Expr {
fn from(agg: AggExpr) -> Self {
Expr::Agg(agg)
}
}
#[derive(Clone, PartialEq)]
pub enum Expr {
Alias(Box<Expr>, Arc<String>),
Column(Arc<String>),
Literal(LiteralValue),
BinaryExpr {
left: Box<Expr>,
op: Operator,
right: Box<Expr>,
},
Not(Box<Expr>),
IsNotNull(Box<Expr>),
IsNull(Box<Expr>),
Cast {
expr: Box<Expr>,
data_type: DataType,
},
Sort {
expr: Box<Expr>,
reverse: bool,
},
SortBy {
expr: Box<Expr>,
by: Box<Expr>,
reverse: bool,
},
Agg(AggExpr),
Ternary {
predicate: Box<Expr>,
truthy: Box<Expr>,
falsy: Box<Expr>,
},
Udf {
input: Box<Expr>,
function: NoEq<Arc<dyn SeriesUdf>>,
output_type: Option<DataType>,
},
Shift {
input: Box<Expr>,
periods: i64,
},
Reverse(Box<Expr>),
Duplicated(Box<Expr>),
IsUnique(Box<Expr>),
Explode(Box<Expr>),
Filter {
input: Box<Expr>,
by: Box<Expr>,
},
Window {
function: Box<Expr>,
partition_by: Box<Expr>,
order_by: Option<Box<Expr>>,
},
Wildcard,
Slice {
input: Box<Expr>,
offset: i64,
length: usize,
},
BinaryFunction {
input_a: Box<Expr>,
input_b: Box<Expr>,
function: NoEq<Arc<dyn SeriesBinaryUdf>>,
output_field: NoEq<Arc<dyn BinaryUdfOutputField>>,
},
Except(Box<Expr>),
}
impl Expr {
pub(crate) fn to_field(&self, schema: &Schema, ctxt: Context) -> Result<Field> {
let mut arena = Arena::with_capacity(5);
let root = to_aexpr(self.clone(), &mut arena);
arena.get(root).to_field(schema, ctxt, &arena)
}
}
impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Expr::*;
match self {
Window {
function,
partition_by,
order_by,
} => write!(
f,
"{:?} OVER (PARTITION BY {:?} ORDER BY {:?}",
function, partition_by, order_by
),
IsUnique(expr) => write!(f, "UNIQUE {:?}", expr),
Explode(expr) => write!(f, "EXPLODE {:?}", expr),
Duplicated(expr) => write!(f, "DUPLICATED {:?}", expr),
Reverse(expr) => write!(f, "REVERSE {:?}", expr),
Alias(expr, name) => write!(f, "{:?} AS {}", expr, name),
Column(name) => write!(f, "{}", name),
Literal(v) => write!(f, "{:?}", v),
BinaryExpr { left, op, right } => write!(f, "[({:?}) {:?} ({:?})]", left, op, right),
Not(expr) => write!(f, "NOT {:?}", expr),
IsNull(expr) => write!(f, "{:?} IS NULL", expr),
IsNotNull(expr) => write!(f, "{:?} IS NOT NULL", expr),
Sort { expr, reverse } => match reverse {
true => write!(f, "{:?} DESC", expr),
false => write!(f, "{:?} ASC", expr),
},
SortBy { expr, by, reverse } => match reverse {
true => write!(f, "{:?} DESC BY {:?}", expr, by),
false => write!(f, "{:?} ASC BY {:?}", expr, by),
},
Filter { input, by } => {
write!(f, "FILTER {:?} BY {:?}", input, by)
}
Agg(agg) => {
use AggExpr::*;
match agg {
Min(expr) => write!(f, "AGG MIN {:?}", expr),
Max(expr) => write!(f, "AGG MAX {:?}", expr),
Median(expr) => write!(f, "AGG MEDIAN {:?}", expr),
Mean(expr) => write!(f, "AGG MEAN {:?}", expr),
First(expr) => write!(f, "AGG FIRST {:?}", expr),
Last(expr) => write!(f, "AGG LAST {:?}", expr),
List(expr) => write!(f, "AGG LIST {:?}", expr),
NUnique(expr) => write!(f, "AGG N UNIQUE {:?}", expr),
Sum(expr) => write!(f, "AGG SUM {:?}", expr),
AggGroups(expr) => write!(f, "AGG GROUPS {:?}", expr),
Count(expr) => write!(f, "AGG COUNT {:?}", expr),
Var(expr) => write!(f, "AGG VAR {:?}", expr),
Std(expr) => write!(f, "AGG STD {:?}", expr),
Quantile { expr, .. } => write!(f, "AGG QUANTILE {:?}", expr),
}
}
Cast { expr, data_type } => write!(f, "CAST {:?} TO {:?}", expr, data_type),
Ternary {
predicate,
truthy,
falsy,
} => write!(
f,
"\nWHEN {:?}\n\t{:?}\nOTHERWISE\n\t{:?}",
predicate, truthy, falsy
),
Udf { input, .. } => write!(f, "APPLY({:?})", input),
BinaryFunction {
input_a, input_b, ..
} => write!(f, "BinaryFunction({:?}, {:?})", input_a, input_b),
Shift { input, periods, .. } => write!(f, "SHIFT {:?} by {}", input, periods),
Slice {
input,
offset,
length,
} => write!(f, "SLICE {:?} offset: {} len: {}", input, offset, length),
Wildcard => write!(f, "*"),
Except(column) => write!(f, "EXCEPT {:?}", column),
}
}
}
pub fn except(name: &str) -> Expr {
match name {
"*" => panic!("cannot use a wildcard as a column exception"),
_ => Expr::Except(Box::new(col(name))),
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Operator {
Eq,
NotEq,
Lt,
LtEq,
Gt,
GtEq,
Plus,
Minus,
Multiply,
Divide,
Modulus,
And,
Or,
}
pub fn binary_expr(l: Expr, op: Operator, r: Expr) -> Expr {
Expr::BinaryExpr {
left: Box::new(l),
op,
right: Box::new(r),
}
}
pub struct When {
predicate: Expr,
}
pub struct WhenThen {
predicate: Expr,
then: Expr,
}
impl When {
pub fn then(self, expr: Expr) -> WhenThen {
WhenThen {
predicate: self.predicate,
then: expr,
}
}
}
impl WhenThen {
pub fn otherwise(self, expr: Expr) -> Expr {
Expr::Ternary {
predicate: Box::new(self.predicate),
truthy: Box::new(self.then),
falsy: Box::new(expr),
}
}
}
pub fn when(predicate: Expr) -> When {
When { predicate }
}
pub fn ternary_expr(predicate: Expr, truthy: Expr, falsy: Expr) -> Expr {
Expr::Ternary {
predicate: Box::new(predicate),
truthy: Box::new(truthy),
falsy: Box::new(falsy),
}
}
impl Expr {
pub fn eq(self, other: Expr) -> Expr {
binary_expr(self, Operator::Eq, other)
}
pub fn neq(self, other: Expr) -> Expr {
binary_expr(self, Operator::NotEq, other)
}
pub fn lt(self, other: Expr) -> Expr {
binary_expr(self, Operator::Lt, other)
}
pub fn gt(self, other: Expr) -> Expr {
binary_expr(self, Operator::Gt, other)
}
pub fn gt_eq(self, other: Expr) -> Expr {
binary_expr(self, Operator::GtEq, other)
}
pub fn lt_eq(self, other: Expr) -> Expr {
binary_expr(self, Operator::LtEq, other)
}
#[allow(clippy::should_implement_trait)]
pub fn not(self) -> Expr {
Expr::Not(Box::new(self))
}
pub fn alias(self, name: &str) -> Expr {
Expr::Alias(Box::new(self), Arc::new(name.into()))
}
#[allow(clippy::wrong_self_convention)]
pub fn is_null(self) -> Self {
Expr::IsNull(Box::new(self))
}
#[allow(clippy::wrong_self_convention)]
pub fn is_not_null(self) -> Self {
Expr::IsNotNull(Box::new(self))
}
pub fn min(self) -> Self {
AggExpr::Min(Box::new(self)).into()
}
pub fn max(self) -> Self {
AggExpr::Max(Box::new(self)).into()
}
pub fn mean(self) -> Self {
AggExpr::Mean(Box::new(self)).into()
}
pub fn median(self) -> Self {
AggExpr::Median(Box::new(self)).into()
}
pub fn sum(self) -> Self {
AggExpr::Sum(Box::new(self)).into()
}
pub fn n_unique(self) -> Self {
AggExpr::NUnique(Box::new(self)).into()
}
pub fn first(self) -> Self {
AggExpr::First(Box::new(self)).into()
}
pub fn last(self) -> Self {
AggExpr::Last(Box::new(self)).into()
}
pub fn list(self) -> Self {
AggExpr::List(Box::new(self)).into()
}
pub fn quantile(self, quantile: f64) -> Self {
AggExpr::Quantile {
expr: Box::new(self),
quantile,
}
.into()
}
pub fn agg_groups(self) -> Self {
AggExpr::AggGroups(Box::new(self)).into()
}
pub fn explode(self) -> Self {
Expr::Explode(Box::new(self))
}
pub fn slice(self, offset: i64, length: usize) -> Self {
Expr::Slice {
input: Box::new(self),
offset,
length,
}
}
pub fn head(self, length: Option<usize>) -> Self {
self.slice(0, length.unwrap_or(10))
}
pub fn tail(self, length: Option<usize>) -> Self {
let len = length.unwrap_or(10);
self.slice(-(len as i64), len)
}
pub fn cast(self, data_type: DataType) -> Self {
Expr::Cast {
expr: Box::new(self),
data_type,
}
}
pub fn sort(self, reverse: bool) -> Self {
Expr::Sort {
expr: Box::new(self),
reverse,
}
}
pub fn reverse(self) -> Self {
Expr::Reverse(Box::new(self))
}
pub fn map<F>(self, function: F, output_type: Option<DataType>) -> Self
where
F: SeriesUdf + 'static,
{
Expr::Udf {
input: Box::new(self),
function: NoEq::new(Arc::new(function)),
output_type,
}
}
#[allow(clippy::wrong_self_convention)]
pub fn is_finite(self) -> Self {
self.map(
|s: Series| s.is_finite().map(|ca| ca.into_series()),
Some(DataType::Boolean),
)
}
#[allow(clippy::wrong_self_convention)]
pub fn is_infinite(self) -> Self {
self.map(
|s: Series| s.is_infinite().map(|ca| ca.into_series()),
Some(DataType::Boolean),
)
}
#[allow(clippy::wrong_self_convention)]
pub fn is_nan(self) -> Self {
self.map(
|s: Series| s.is_nan().map(|ca| ca.into_series()),
Some(DataType::Boolean),
)
}
#[allow(clippy::wrong_self_convention)]
pub fn is_not_nan(self) -> Self {
self.map(
|s: Series| s.is_not_nan().map(|ca| ca.into_series()),
Some(DataType::Boolean),
)
}
pub fn shift(self, periods: i64) -> Self {
Expr::Shift {
input: Box::new(self),
periods,
}
}
pub fn shift_and_fill(self, periods: i64, fill_value: Expr) -> Self {
if periods > 0 {
when(self.clone().map(
move |s: Series| {
let ca: BooleanChunked = (0..s.len() as i64).map(|i| i >= periods).collect();
Ok(ca.into_series())
},
Some(DataType::Boolean),
))
.then(self.shift(periods))
.otherwise(fill_value)
} else {
when(self.clone().map(
move |s: Series| {
let length = s.len() as i64;
let tipping_point = length + periods;
let ca: BooleanChunked = (0..length).map(|i| i < tipping_point).collect();
Ok(ca.into_series())
},
Some(DataType::Boolean),
))
.then(self.shift(periods))
.otherwise(fill_value)
}
}
pub fn cum_sum(self, reverse: bool) -> Self {
self.map(move |s: Series| Ok(s.cum_sum(reverse)), None)
}
pub fn cum_min(self, reverse: bool) -> Self {
self.map(move |s: Series| Ok(s.cum_min(reverse)), None)
}
pub fn cum_max(self, reverse: bool) -> Self {
self.map(move |s: Series| Ok(s.cum_max(reverse)), None)
}
pub fn over(self, partition_by: Expr) -> Self {
Expr::Window {
function: Box::new(self),
partition_by: Box::new(partition_by),
order_by: None,
}
}
pub fn fill_none(self, fill_value: Expr) -> Self {
let name = output_name(&self).unwrap();
when(self.is_null())
.then(fill_value)
.otherwise(col(&*name))
.alias(&*name)
}
pub fn count(self) -> Self {
AggExpr::Count(Box::new(self)).into()
}
pub fn std(self) -> Self {
AggExpr::Std(Box::new(self)).into()
}
pub fn var(self) -> Self {
AggExpr::Var(Box::new(self)).into()
}
#[allow(clippy::wrong_self_convention)]
pub fn is_duplicated(self) -> Self {
Expr::Duplicated(Box::new(self))
}
#[allow(clippy::wrong_self_convention)]
pub fn is_unique(self) -> Self {
Expr::IsUnique(Box::new(self))
}
pub fn and(self, expr: Expr) -> Self {
binary_expr(self, Operator::And, expr)
}
pub fn or(self, expr: Expr) -> Self {
binary_expr(self, Operator::Or, expr)
}
pub fn pow(self, exponent: f64) -> Self {
self.map(move |s: Series| s.pow(exponent), Some(DataType::Float64))
}
pub fn filter(self, predicate: Expr) -> Self {
if has_expr(&self, |e| matches!(e, Expr::Wildcard)) {
panic!("filter '*' not allowed, use LazyFrame::filter")
};
Expr::Filter {
input: Box::new(self),
by: Box::new(predicate),
}
}
#[allow(clippy::wrong_self_convention)]
pub fn is_in(self, list_expr: Expr) -> Self {
map_binary(
self,
list_expr,
|left, list_ex| {
let list_array = list_ex.list()?;
left.is_in(list_array).map(|ca| ca.into_series())
},
Some(Field::new("", DataType::Boolean)),
)
}
#[cfg(feature = "temporal")]
pub fn year(self) -> Expr {
let function = move |s: Series| s.year().map(|ca| ca.into_series());
self.map(function, Some(DataType::UInt32))
}
#[cfg(feature = "temporal")]
pub fn month(self) -> Expr {
let function = move |s: Series| s.month().map(|ca| ca.into_series());
self.map(function, Some(DataType::UInt32))
}
#[cfg(feature = "temporal")]
pub fn day(self) -> Expr {
let function = move |s: Series| s.day().map(|ca| ca.into_series());
self.map(function, Some(DataType::UInt32))
}
#[cfg(feature = "temporal")]
pub fn ordinal_day(self) -> Expr {
let function = move |s: Series| s.ordinal_day().map(|ca| ca.into_series());
self.map(function, Some(DataType::UInt32))
}
#[cfg(feature = "temporal")]
pub fn hour(self) -> Expr {
let function = move |s: Series| s.hour().map(|ca| ca.into_series());
self.map(function, Some(DataType::UInt32))
}
#[cfg(feature = "temporal")]
pub fn minute(self) -> Expr {
let function = move |s: Series| s.minute().map(|ca| ca.into_series());
self.map(function, Some(DataType::UInt32))
}
#[cfg(feature = "temporal")]
pub fn second(self) -> Expr {
let function = move |s: Series| s.second().map(|ca| ca.into_series());
self.map(function, Some(DataType::UInt32))
}
#[cfg(feature = "temporal")]
pub fn nanosecond(self) -> Expr {
let function = move |s: Series| s.nanosecond().map(|ca| ca.into_series());
self.map(function, Some(DataType::UInt32))
}
pub fn sort_by(self, by: Expr, reverse: bool) -> Expr {
Expr::SortBy {
expr: Box::new(self),
by: Box::new(by),
reverse,
}
}
}
pub fn col(name: &str) -> Expr {
match name {
"*" => Expr::Wildcard,
_ => Expr::Column(Arc::new(name.to_owned())),
}
}
pub fn count(name: &str) -> Expr {
match name {
"" => col(name).count().alias("count"),
_ => col(name).count(),
}
}
pub fn sum(name: &str) -> Expr {
col(name).sum()
}
pub fn min(name: &str) -> Expr {
col(name).min()
}
pub fn max(name: &str) -> Expr {
col(name).max()
}
pub fn mean(name: &str) -> Expr {
col(name).mean()
}
pub fn avg(name: &str) -> Expr {
col(name).mean()
}
pub fn median(name: &str) -> Expr {
col(name).median()
}
pub fn quantile(name: &str, quantile: f64) -> Expr {
col(name).quantile(quantile)
}
pub fn map_binary<F: 'static>(a: Expr, b: Expr, f: F, output_field: Option<Field>) -> Expr
where
F: Fn(Series, Series) -> Result<Series> + Send + Sync,
{
let output_field = move |_: &Schema, _: Context, _: &Field, _: &Field| output_field.clone();
Expr::BinaryFunction {
input_a: Box::new(a),
input_b: Box::new(b),
function: NoEq::new(Arc::new(f)),
output_field: NoEq::new(Arc::new(output_field)),
}
}
pub fn map_binary_lazy_field<F: 'static, Fld: 'static>(
a: Expr,
b: Expr,
f: F,
output_field: Fld,
) -> Expr
where
F: Fn(Series, Series) -> Result<Series> + Send + Sync,
Fld: Fn(&Schema, Context, &Field, &Field) -> Option<Field> + Send + Sync,
{
Expr::BinaryFunction {
input_a: Box::new(a),
input_b: Box::new(b),
function: NoEq::new(Arc::new(f)),
output_field: NoEq::new(Arc::new(output_field)),
}
}
pub fn fold_exprs<F: 'static>(mut acc: Expr, f: F, exprs: Vec<Expr>) -> Expr
where
F: Fn(Series, Series) -> Result<Series> + Send + Sync + Copy,
{
for e in exprs {
acc = map_binary(acc, e, f, None);
}
acc
}
pub fn sum_exprs(exprs: Vec<Expr>) -> Expr {
let func = |s1, s2| Ok(&s1 + &s2);
fold_exprs(lit(0), func, exprs)
}
pub fn max_exprs(exprs: Vec<Expr>) -> Expr {
let func = |s1: Series, s2: Series| {
let mask = s1.gt(&s2);
s1.zip_with(&mask, &s2)
};
fold_exprs(lit(0), func, exprs)
}
pub fn min_exprs(exprs: Vec<Expr>) -> Expr {
let func = |s1: Series, s2: Series| {
let mask = s1.lt(&s2);
s1.zip_with(&mask, &s2)
};
fold_exprs(lit(0), func, exprs)
}
pub fn any_exprs(exprs: Vec<Expr>) -> Expr {
let func = |s1: Series, s2: Series| Ok(s1.bool()?.bitor(s2.bool()?).into_series());
fold_exprs(lit(false), func, exprs)
}
pub fn all_exprs(exprs: Vec<Expr>) -> Expr {
let func = |s1: Series, s2: Series| Ok(s1.bool()?.bitand(s2.bool()?).into_series());
fold_exprs(lit(true), func, exprs)
}
pub trait Literal {
fn lit(self) -> Expr;
}
impl Literal for String {
fn lit(self) -> Expr {
Expr::Literal(LiteralValue::Utf8(self))
}
}
impl<'a> Literal for &'a str {
fn lit(self) -> Expr {
Expr::Literal(LiteralValue::Utf8(self.to_owned()))
}
}
macro_rules! make_literal {
($TYPE:ty, $SCALAR:ident) => {
impl Literal for $TYPE {
fn lit(self) -> Expr {
Expr::Literal(LiteralValue::$SCALAR(self))
}
}
};
}
make_literal!(bool, Boolean);
make_literal!(f32, Float32);
make_literal!(f64, Float64);
#[cfg(feature = "dtype-i8")]
make_literal!(i8, Int8);
#[cfg(feature = "dtype-i16")]
make_literal!(i16, Int16);
make_literal!(i32, Int32);
make_literal!(i64, Int64);
#[cfg(feature = "dtype-u8")]
make_literal!(u8, UInt8);
#[cfg(feature = "dtype-u16")]
make_literal!(u16, UInt16);
make_literal!(u32, UInt32);
#[cfg(feature = "dtype-u64")]
make_literal!(u64, UInt64);
pub struct Null {}
impl Literal for Null {
fn lit(self) -> Expr {
Expr::Literal(LiteralValue::Null)
}
}
#[cfg(all(feature = "temporal", feature = "dtype-date64"))]
impl Literal for NaiveDateTime {
fn lit(self) -> Expr {
Expr::Literal(LiteralValue::DateTime(self))
}
}
#[cfg(all(feature = "temporal", feature = "dtype-date64"))]
impl Literal for NaiveDate {
fn lit(self) -> Expr {
Expr::Literal(LiteralValue::DateTime(self.and_hms(0, 0, 0)))
}
}
impl Literal for Series {
fn lit(self) -> Expr {
Expr::Literal(LiteralValue::Series(NoEq::new(self)))
}
}
pub fn lit<L: Literal>(t: L) -> Expr {
t.lit()
}
pub fn not(expr: Expr) -> Expr {
Expr::Not(Box::new(expr))
}
pub fn is_null(expr: Expr) -> Expr {
Expr::IsNull(Box::new(expr))
}
pub fn is_not_null(expr: Expr) -> Expr {
Expr::IsNotNull(Box::new(expr))
}
pub fn cast(expr: Expr, data_type: DataType) -> Expr {
Expr::Cast {
expr: Box::new(expr),
data_type,
}
}
pub trait Range<T> {
fn into_range(self, high: T) -> Expr;
}
macro_rules! impl_into_range {
($dt: ty) => {
impl Range<$dt> for $dt {
fn into_range(self, high: $dt) -> Expr {
Expr::Literal(LiteralValue::Range {
low: self as i64,
high: high as i64,
data_type: DataType::Int32,
})
}
}
};
}
impl_into_range!(i32);
impl_into_range!(i64);
impl_into_range!(u32);
pub fn range<T: Range<T>>(low: T, high: T) -> Expr {
low.into_range(high)
}
impl Add for Expr {
type Output = Expr;
fn add(self, rhs: Self) -> Self::Output {
binary_expr(self, Operator::Plus, rhs)
}
}
impl Sub for Expr {
type Output = Expr;
fn sub(self, rhs: Self) -> Self::Output {
binary_expr(self, Operator::Minus, rhs)
}
}
impl Div for Expr {
type Output = Expr;
fn div(self, rhs: Self) -> Self::Output {
binary_expr(self, Operator::Divide, rhs)
}
}
impl Mul for Expr {
type Output = Expr;
fn mul(self, rhs: Self) -> Self::Output {
binary_expr(self, Operator::Multiply, rhs)
}
}
impl Rem for Expr {
type Output = Expr;
fn rem(self, rhs: Self) -> Self::Output {
binary_expr(self, Operator::Modulus, rhs)
}
}