use crate::{
col::Col,
query::{
request::{QueryRequest,NewRequest},
filter::{FilterForHeader},
{QueryPlanImpl, QueryPlan},
},
record::{Record, ExternalRecord},
header::{HasCol, ProjectFrom, Header},
relation::{Relation,RelationImpl,Queryable,QueryOutput},
query::{fallback::FallbackPlanner},
};
use tylisp::{
sexpr, defun_nocalc, defun, calc,
typenum as tn,
ops::{
Ret, If,
list::{EmptyP},
},
};
use std::{
marker::PhantomData,
};
pub struct SubordinateJoin<Parent,Field>(Parent, PhantomData<Field>);
impl<P,F> SubordinateJoin<P,F> {
pub(super) fn new(parent:P) -> Self where Self: RelationImpl {
SubordinateJoin(parent, PhantomData)
}
}
impl<Parent, Field, Child> RelationImpl for SubordinateJoin<Parent,Field> where
Parent: RelationImpl,
Parent::Cols: HasCol<Field>,
Field: Col<Inner=Child>,
Child: RelationImpl,
(Parent::Cols, Child::Cols): Record,
{
type Cols = <(Parent::Cols, Child::Cols) as Record>::Cols;
type FastCols = Parent::FastCols;
type Planner = SubJoinPlanner;
}
impl<'a, Parent, Field, Child, OutCols>
QueryOutput<'a> for SubordinateJoin<Parent,Field>
where
Parent: QueryOutput<'a>,
OutCols: Header + HasCol<Field>,
Field: Col<Inner=Child>,
Child: QueryOutput<'a>,
Self: RelationImpl<Cols=OutCols>,
(Parent::QueryRow, Child::QueryRow): ExternalRecord<'a, Cols=OutCols>
{
type QueryRow = (Parent::QueryRow, Child::QueryRow);
}
#[derive(Debug, Default)]
pub struct SubJoinPlanner;
defun_nocalc!{() SubJoinPlanner {
('a, F:Col, P:RelationImpl, Req:QueryRequest)
{ _:&'a SubordinateJoin<P,F>, _:Req }
=> {If, {EmptyP, @Req::OrderBy},
{Ret, @SubJoinPlan<'a, P,F,Req>},
{FallbackPlanner, @&'a SubordinateJoin<P,F>, @Req}};
}}
pub struct SubJoinPlan<'a,P,F,Req> {
join: &'a SubordinateJoin<P,F>,
req: Req
}
impl<'a, Parent, Field, Req>
QueryPlanImpl<'a, SubordinateJoin<Parent,Field>, Req>
for SubJoinPlan<'a, Parent, Field, Req>
where
{
fn prepare(rel: &'a SubordinateJoin<Parent,Field>, req:Req) -> Self {
SubJoinPlan { join: rel, req }
}
}
impl<'a,Parent,Field,Child,Req>
IntoIterator for SubJoinPlan<'a,Parent,Field,Req>
where
Req: QueryRequest + 'a,
Parent:Queryable<'a,Req>,
Child:Queryable<'a,Req>,
Parent::Cols: HasCol<Field>,
Field: Col<Inner=Child>
{
type Item = (Parent::QueryRow, Child::QueryRow);
type IntoIter = impl Iterator<Item = Self::Item>;
fn into_iter(self)->Self::IntoIter {
self.join.0
.query(self.req.clone())
.flat_map(move |parent_row| {
parent_row.ext_col_ref::<Field>()
.query(self.req.clone())
.map(move |child_row| (parent_row, child_row))
})
}
}
impl<'a,Parent,Field,Child>
IntoIterator for &'a SubordinateJoin<Parent,Field>
where
Parent:Relation<'a>,
Child: 'a+Relation<'a>,
Parent::Cols: HasCol<Field>,
Field: Col<Inner=Child>
{
type Item = (Parent::QueryRow, Child::QueryRow);
type IntoIter = impl Iterator<Item = Self::Item>;
fn into_iter(self)->Self::IntoIter {
self.0
.iter_all()
.flat_map(move |parent_row| {
parent_row.ext_col_ref::<Field>()
.iter_all()
.map(move |child_row| (parent_row, child_row))
})
}
}
#[cfg(test)] mod test {
use super::*;
use crate::relation::SelfQuery;
#[test]
fn test_subjoin_plan() {
col!{A: usize}
col!{B: usize}
col!{C: usize}
col!{CVec: Vec<C>}
assert_trait!{Vec<C>: RelationImpl}
assert_trait!{Vec<(A, CVec)>: RelationImpl}
use crate::query::request::*;
use crate::query::filter::Exact;
use crate::relation::RelProxy;
type Join<'a> = SubordinateJoin<RelProxy<&'a Vec<(A, CVec)>>, CVec>;
let rel: Vec<(A, CVec)> = vec![
(A(1), CVec(vec![C(3), C(6)])),
(A(2), CVec(vec![C(6)])),
];
assert_trait!{Join: RelationImpl}
let join: Join = SubordinateJoin::new(RelationImpl::as_ref(&rel));
assert_eq!(3, join.iter_all().count());
assert_eq!(2, join.as_ref().where_eq(C(6)).iter_all().count());
assert_eq!(2, join.as_ref().where_eq(A(1)).iter_all().count());
}
}