use std::rc::Rc;
use crate::{
Binding, CombinedProjection, Element, FindSpec, ProjectedElements, QueryOutput, QueryResults,
RelResult, RowSource, Schema, SqlConn, SqlRow, TypedIndex,
};
use query_projector_traits::errors::Result;
use super::Projector;
pub(crate) struct ScalarProjector {
spec: Rc<FindSpec>,
template: TypedIndex,
}
impl ScalarProjector {
fn with_template(spec: Rc<FindSpec>, template: TypedIndex) -> ScalarProjector {
ScalarProjector { spec, template }
}
pub(crate) fn combine(
spec: Rc<FindSpec>,
mut elements: ProjectedElements,
) -> Result<CombinedProjection> {
let template = elements
.templates
.pop()
.expect("Expected a single template");
let projector = Box::new(ScalarProjector::with_template(spec, template));
let distinct = false;
elements.combine(projector, distinct)
}
}
impl Projector for ScalarProjector {
fn project(
&self,
_schema: &Schema,
_sqlite: &dyn SqlConn,
rows: &mut dyn RowSource,
) -> Result<QueryOutput> {
let results = if let Some(r) = rows.next_row()? {
let row = r;
let binding = self.template.lookup(row)?;
QueryResults::Scalar(Some(binding))
} else {
QueryResults::Scalar(None)
};
Ok(QueryOutput {
spec: self.spec.clone(),
results,
})
}
fn columns<'s>(&'s self) -> Box<dyn Iterator<Item = &'s Element> + 's> {
self.spec.columns()
}
}
pub(crate) struct TupleProjector {
spec: Rc<FindSpec>,
len: usize,
templates: Vec<TypedIndex>,
}
impl TupleProjector {
fn with_templates(
spec: Rc<FindSpec>,
len: usize,
templates: Vec<TypedIndex>,
) -> TupleProjector {
TupleProjector {
spec,
len,
templates,
}
}
fn collect_bindings(&self, row: &dyn SqlRow) -> Result<Vec<Binding>> {
assert!(row.column_count() >= self.len);
self.templates
.iter()
.map(|ti| ti.lookup(row))
.collect::<Result<Vec<Binding>>>()
}
pub(crate) fn combine(
spec: Rc<FindSpec>,
column_count: usize,
mut elements: ProjectedElements,
) -> Result<CombinedProjection> {
let projector = Box::new(TupleProjector::with_templates(
spec,
column_count,
elements.take_templates(),
));
let distinct = false;
elements.combine(projector, distinct)
}
}
impl Projector for TupleProjector {
fn project(
&self,
_schema: &Schema,
_sqlite: &dyn SqlConn,
rows: &mut dyn RowSource,
) -> Result<QueryOutput> {
let results = if let Some(r) = rows.next_row()? {
let row = r;
let bindings = self.collect_bindings(row)?;
QueryResults::Tuple(Some(bindings))
} else {
QueryResults::Tuple(None)
};
Ok(QueryOutput {
spec: self.spec.clone(),
results,
})
}
fn columns<'s>(&'s self) -> Box<dyn Iterator<Item = &'s Element> + 's> {
self.spec.columns()
}
}
pub(crate) struct RelProjector {
spec: Rc<FindSpec>,
len: usize,
templates: Vec<TypedIndex>,
}
impl RelProjector {
fn with_templates(spec: Rc<FindSpec>, len: usize, templates: Vec<TypedIndex>) -> RelProjector {
RelProjector {
spec,
len,
templates,
}
}
fn collect_bindings_into(&self, row: &dyn SqlRow, out: &mut Vec<Binding>) -> Result<()> {
assert!(row.column_count() >= self.len);
let mut count = 0;
for binding in self.templates.iter().map(|ti| ti.lookup(row)) {
out.push(binding?);
count += 1;
}
assert_eq!(self.len, count);
Ok(())
}
pub(crate) fn combine(
spec: Rc<FindSpec>,
column_count: usize,
mut elements: ProjectedElements,
) -> Result<CombinedProjection> {
let projector = Box::new(RelProjector::with_templates(
spec,
column_count,
elements.take_templates(),
));
let already_distinct =
elements.pre_aggregate_projection.is_some() || projector.columns().all(|e| e.is_unit());
elements.combine(projector, !already_distinct)
}
}
impl Projector for RelProjector {
fn project(
&self,
_schema: &Schema,
_sqlite: &dyn SqlConn,
rows: &mut dyn RowSource,
) -> Result<QueryOutput> {
let width = self.len;
let mut values: Vec<_> = Vec::with_capacity(5 * width);
while let Some(r) = rows.next_row()? {
let row = r;
self.collect_bindings_into(row, &mut values)?;
}
Ok(QueryOutput {
spec: self.spec.clone(),
results: QueryResults::Rel(RelResult { width, values }),
})
}
fn columns<'s>(&'s self) -> Box<dyn Iterator<Item = &'s Element> + 's> {
self.spec.columns()
}
}
pub(crate) struct CollProjector {
spec: Rc<FindSpec>,
template: TypedIndex,
}
impl CollProjector {
fn with_template(spec: Rc<FindSpec>, template: TypedIndex) -> CollProjector {
CollProjector { spec, template }
}
pub(crate) fn combine(
spec: Rc<FindSpec>,
mut elements: ProjectedElements,
) -> Result<CombinedProjection> {
let template = elements
.templates
.pop()
.expect("Expected a single template");
let projector = Box::new(CollProjector::with_template(spec, template));
let already_distinct =
elements.pre_aggregate_projection.is_some() || projector.columns().all(|e| e.is_unit());
elements.combine(projector, !already_distinct)
}
}
impl Projector for CollProjector {
fn project(
&self,
_schema: &Schema,
_sqlite: &dyn SqlConn,
rows: &mut dyn RowSource,
) -> Result<QueryOutput> {
let mut out: Vec<_> = vec![];
while let Some(r) = rows.next_row()? {
let row = r;
let binding = self.template.lookup(row)?;
out.push(binding);
}
Ok(QueryOutput {
spec: self.spec.clone(),
results: QueryResults::Coll(out),
})
}
fn columns<'s>(&'s self) -> Box<dyn Iterator<Item = &'s Element> + 's> {
self.spec.columns()
}
}