use crate::{
relation::{RelationImpl,Queryable,QueryOutput,Delete},
query::{
QueryPlanImpl,
request::{QueryRequest, NewRequest, BlankRequest, AddFilter},
filter::{QueryFilter, FilterForHeader},
},
transaction::{Transaction,UndoLog,RevertableOp},
};
use tylisp::{
sexpr, sexpr_val, sexpr_quoted_types, calc, defun, HNil,
marker_traits::{Pass, List},
ops::{
Phantom, Ret,
list::{Concat},
},
};
use std::marker::PhantomData;
pub struct FilterRel<Rel, Filt> {
pub(crate) source: Rel,
pub(crate) filter: Filt,
}
impl<Rel, Filt> RelationImpl for FilterRel<Rel,Filt>
where
Rel: RelationImpl,
Filt: QueryFilter,
sexpr!{FilterForHeader, @Rel::Cols, @Filt}: Pass
{
type Cols = Rel::Cols;
type FastCols = Rel::FastCols;
type Planner = Planner;
}
impl<'a, Rel, Filt> QueryOutput<'a> for FilterRel<Rel,Filt>
where
Rel: QueryOutput<'a>,
Self: RelationImpl<Cols = Rel::Cols>,
{
type QueryRow = Rel::QueryRow;
}
impl<'a,R,F> IntoIterator for &'a FilterRel<R,F>
where
F: QueryFilter,
AddFilter<BlankRequest,F>: QueryRequest,
R:Queryable<'a, AddFilter<BlankRequest,F>>
{
type Item = R::QueryRow;
type IntoIter = impl Iterator<Item = Self::Item>;
fn into_iter(self)->Self::IntoIter {
self.source.query(
BlankRequest.add_filter(self.filter.clone())
)
}
}
pub struct FilteredRequest<'a,R,F,Q> {
rel: &'a FilterRel<R,F>,
q: Q
}
impl<'a,R,F,Q>
IntoIterator for FilteredRequest<'a,R,F,Q>
where
F: QueryFilter,
R: Queryable<'a, AddFilter<Q,F>>,
Q: QueryRequest + 'a,
AddFilter<Q,F>: QueryRequest
{
type Item = R::QueryRow;
type IntoIter = impl Iterator<Item = Self::Item>;
fn into_iter(self)->Self::IntoIter {
self.rel.source.query(
self.q.add_filter(self.rel.filter.clone())
)
}
}
impl<'a,R,F,Q>
QueryPlanImpl<'a, FilterRel<R,F>, Q> for FilteredRequest<'a,R,F,Q>
where
{
fn prepare(rel: &'a FilterRel<R,F>, q:Q)->Self {
FilteredRequest { rel, q }
}
}
#[derive(Default,Copy,Clone,Debug)]
pub struct Planner;
defun!{ Planner {
('a, Rel, Filt: QueryFilter + Clone, Req: QueryRequest)
{fr: &'a FilterRel<Rel, Filt>, req: Req}
=> { Ret, @FilteredRequest<'a, Rel, Filt, Req> };
}}
impl<R,F,Q> Delete<Q> for FilterRel<R,F>
where
R:Delete<sexpr!{F,Q}>,
Q: QueryFilter,
F: QueryFilter,
sexpr!{F,Q}: QueryFilter,
{
type Op = DeleteOp<Q, R::Deleter>;
type Deleter = impl Fn(Q)->Self::Op;
fn deleter()->Self::Deleter {
|q| DeleteOp{ q, deleter: R::deleter() }
}
}
pub struct DeleteOp<Q,Deleter> {
q: Q,
deleter: Deleter
}
impl<R,F,Q,Deleter,Op> RevertableOp<FilterRel<R,F>> for DeleteOp<Q,Deleter>
where
Deleter: Fn(sexpr!{F, Q})->Op,
Op: RevertableOp<R>,
F: QueryFilter,
Q: QueryFilter
{
type Err = Op::Err;
type Log = impl UndoLog<FilterRel<R,F>>;
fn apply(self, rel: &mut FilterRel<R,F>)->Result<Self::Log, Self::Err> {
let op = (self.deleter)(sexpr_val!{rel.filter.clone(), self.q});
let inner_log =
Transaction::start(&mut rel.source)
.apply(op)
.into_undo_log()
.map_err(|e| e.left().unwrap())?;
Ok(UndoInner(inner_log))
}
}
pub struct UndoInner<Log>(Log);
impl<R,F,Log> UndoLog<FilterRel<R,F>> for UndoInner<Log>
where Log: UndoLog<R> {
fn revert(self, rel: &mut FilterRel<R,F>) {
self.0.revert(&mut rel.source)
}
}
#[test]
fn test_filter_rel() {
col!{A: usize}
col!{B: usize}
use crate::relation::{Relation,OpaqueRel};
use crate::record::Record;
let source: OpaqueRel<Vec<(A,B)>> = OpaqueRel::new(vec![ (A(3), B(6)), (A(5), B(2)), (A(3), B(7)) ]);
let result: Vec<usize> = source.as_ref().where_eq(A(3)).into_iter().map(|r| **r.col_ref::<B>()).collect();
assert_eq!(vec![6usize,7], result);
let result: Vec<usize> = source.where_eq(A(3)).into_iter().map(|r| **r.col_ref::<B>()).collect();
assert_eq!(vec![6usize,7], result);
}
#[test]
fn test_filter_rel_ref() {
col!{A: usize}
col!{B: usize}
use crate::relation::{Relation,OpaqueRel};
use crate::record::Record;
let source: OpaqueRel<Vec<(A,B)>> = OpaqueRel::new(vec![ (A(3), B(6)), (A(5), B(2)), (A(3), B(7)) ]);
let result: Vec<usize> = source.as_ref().where_eq(&A(3)).into_iter().map(|r| **r.col_ref::<B>()).collect();
assert_eq!(vec![6usize,7], result);
let result: Vec<usize> = source.where_eq(&A(3)).into_iter().map(|r| **r.col_ref::<B>()).collect();
assert_eq!(vec![6usize,7], result);
}