use async_recursion::async_recursion;
use boolinator::Boolinator;
use futures::stream::{self, StreamExt, TryStreamExt};
use im_rc::HashMap;
use serde::Serialize;
use std::fmt::Debug;
use std::rc::Rc;
use thiserror::Error;
use sqlparser::ast::{BinaryOperator, Expr, Function, UnaryOperator};
use super::context::{BlendContext, FilterContext};
use super::evaluate::{evaluate, Evaluated};
use super::select::select;
use crate::data::Value;
use crate::result::Result;
use crate::store::Store;
#[derive(Error, Serialize, Debug, PartialEq)]
pub enum FilterError {
#[error("unimplemented")]
Unimplemented,
}
pub struct Filter<'a, T: 'static + Debug> {
storage: &'a dyn Store<T>,
where_clause: Option<&'a Expr>,
context: Option<Rc<FilterContext<'a>>>,
aggregated: Option<Rc<HashMap<&'a Function, Value>>>,
}
impl<'a, T: 'static + Debug> Filter<'a, T> {
pub fn new(
storage: &'a dyn Store<T>,
where_clause: Option<&'a Expr>,
context: Option<Rc<FilterContext<'a>>>,
aggregated: Option<Rc<HashMap<&'a Function, Value>>>,
) -> Self {
Self {
storage,
where_clause,
context,
aggregated,
}
}
pub async fn check(&self, blend_context: Rc<BlendContext<'a>>) -> Result<bool> {
match self.where_clause {
Some(expr) => {
let context = self.context.as_ref().map(Rc::clone);
let context = FilterContext::concat(context, Some(blend_context));
let context = Some(context).map(Rc::new);
let aggregated = self.aggregated.as_ref().map(Rc::clone);
check_expr(self.storage, context, aggregated, expr).await
}
None => Ok(true),
}
}
}
#[async_recursion(?Send)]
pub async fn check_expr<T: 'static + Debug>(
storage: &dyn Store<T>,
filter_context: Option<Rc<FilterContext<'async_recursion>>>,
aggregated: Option<Rc<HashMap<&'async_recursion Function, Value>>>,
expr: &Expr,
) -> Result<bool> {
let evaluate = |expr: &'async_recursion Expr| {
let filter_context = filter_context.as_ref().map(Rc::clone);
let aggregated = aggregated.as_ref().map(Rc::clone);
evaluate(storage, filter_context, aggregated, expr, false)
};
let check = |expr| {
let filter_context = filter_context.as_ref().map(Rc::clone);
let aggregated = aggregated.as_ref().map(Rc::clone);
check_expr(storage, filter_context, aggregated, expr)
};
match expr {
Expr::BinaryOp { op, left, right } => {
let zip_evaluate = || async move {
let l = evaluate(left).await?;
let r = evaluate(right).await?;
Ok((l, r))
};
let zip_check = || async move {
let l = check(left).await?;
let r = check(right).await?;
Ok((l, r))
};
match op {
BinaryOperator::Eq => zip_evaluate().await.map(|(l, r)| l == r),
BinaryOperator::NotEq => zip_evaluate().await.map(|(l, r)| l != r),
BinaryOperator::And => zip_check().await.map(|(l, r)| l && r),
BinaryOperator::Or => zip_check().await.map(|(l, r)| l || r),
BinaryOperator::Lt => zip_evaluate().await.map(|(l, r)| l < r),
BinaryOperator::LtEq => zip_evaluate().await.map(|(l, r)| l <= r),
BinaryOperator::Gt => zip_evaluate().await.map(|(l, r)| l > r),
BinaryOperator::GtEq => zip_evaluate().await.map(|(l, r)| l >= r),
_ => Err(FilterError::Unimplemented.into()),
}
}
Expr::UnaryOp { op, expr } => match op {
UnaryOperator::Not => check(&expr).await.map(|v| !v),
_ => Err(FilterError::Unimplemented.into()),
},
Expr::Nested(expr) => check(&expr).await,
Expr::InList {
expr,
list,
negated,
} => {
let negated = *negated;
let target = evaluate(expr).await?;
stream::iter(list.iter())
.filter_map(|expr| {
let target = ⌖
async move {
evaluate(expr).await.map_or_else(
|error| Some(Err(error)),
|evaluated| (target == &evaluated).as_some(Ok(!negated)),
)
}
})
.take(1)
.collect::<Vec<_>>()
.await
.into_iter()
.next()
.unwrap_or(Ok(negated))
}
Expr::InSubquery {
expr,
subquery,
negated,
} => {
let target = evaluate(expr).await?;
select(storage, &subquery, filter_context)
.await?
.try_filter_map(|row| {
let target = ⌖
async move {
let value = row.take_first_value()?;
(target == &Evaluated::ValueRef(&value))
.as_some(Ok(!negated))
.transpose()
}
})
.take(1)
.collect::<Vec<_>>()
.await
.into_iter()
.next()
.unwrap_or(Ok(*negated))
}
Expr::Between {
expr,
negated,
low,
high,
} => {
let negated = *negated;
let target = evaluate(expr).await?;
Ok(negated ^ (evaluate(low).await? <= target && target <= evaluate(high).await?))
}
Expr::Exists(query) => Ok(select(storage, query, filter_context)
.await?
.into_stream()
.take(1)
.try_collect::<Vec<_>>()
.await?
.get(0)
.is_some()),
Expr::IsNull(expr) => Ok(!evaluate(expr).await?.is_some()),
Expr::IsNotNull(expr) => Ok(evaluate(expr).await?.is_some()),
_ => Err(FilterError::Unimplemented.into()),
}
}