This commit is contained in:
Gregory Burd 2020-02-21 10:27:39 -05:00
parent 58e06742fd
commit dfb5866174
9 changed files with 146 additions and 163 deletions

View file

@ -28,14 +28,14 @@ pub enum SimpleAggregationOp {
} }
impl SimpleAggregationOp { impl SimpleAggregationOp {
pub fn to_sql(&self) -> &'static str { pub fn to_sql(self) -> &'static str {
use self::SimpleAggregationOp::*; use self::SimpleAggregationOp::*;
match self { match self {
&Avg => "avg", Avg => "avg",
&Count => "count", Count => "count",
&Max => "max", Max => "max",
&Min => "min", Min => "min",
&Sum => "sum", Sum => "sum",
} }
} }
@ -57,29 +57,29 @@ impl SimpleAggregationOp {
/// but invalid to take `Max` of `{Uuid, String}`. /// but invalid to take `Max` of `{Uuid, String}`.
/// ///
/// The returned type is the type of the result of the aggregation. /// The returned type is the type of the result of the aggregation.
pub fn is_applicable_to_types(&self, possibilities: ValueTypeSet) -> Result<ValueType> { pub fn is_applicable_to_types(self, possibilities: ValueTypeSet) -> Result<ValueType> {
use self::SimpleAggregationOp::*; use self::SimpleAggregationOp::*;
if possibilities.is_empty() { if possibilities.is_empty() {
bail!(ProjectorError::CannotProjectImpossibleBinding(*self)) bail!(ProjectorError::CannotProjectImpossibleBinding(self))
} }
match self { match self {
// One can always count results. // One can always count results.
&Count => Ok(ValueType::Long), Count => Ok(ValueType::Long),
// Only numeric types can be averaged or summed. // Only numeric types can be averaged or summed.
&Avg => { Avg => {
if possibilities.is_only_numeric() { if possibilities.is_only_numeric() {
// The mean of a set of numeric values will always, for our purposes, be a double. // The mean of a set of numeric values will always, for our purposes, be a double.
Ok(ValueType::Double) Ok(ValueType::Double)
} else { } else {
bail!(ProjectorError::CannotApplyAggregateOperationToTypes( bail!(ProjectorError::CannotApplyAggregateOperationToTypes(
*self, self,
possibilities possibilities
)) ))
} }
} }
&Sum => { Sum => {
if possibilities.is_only_numeric() { if possibilities.is_only_numeric() {
if possibilities.contains(ValueType::Double) { if possibilities.contains(ValueType::Double) {
Ok(ValueType::Double) Ok(ValueType::Double)
@ -89,18 +89,18 @@ impl SimpleAggregationOp {
} }
} else { } else {
bail!(ProjectorError::CannotApplyAggregateOperationToTypes( bail!(ProjectorError::CannotApplyAggregateOperationToTypes(
*self, self,
possibilities possibilities
)) ))
} }
} }
&Max | &Min => { Max | Min => {
if possibilities.is_unit() { if possibilities.is_unit() {
use self::ValueType::*; use self::ValueType::*;
let the_type = possibilities.exemplar().expect("a type"); let the_type = possibilities.exemplar().expect("a type");
match the_type { match the_type {
// These types are numerically ordered. // Numerically ordered types.
Double | Long | Instant => Ok(the_type), Double | Long | Instant => Ok(the_type),
// Boolean: false < true. // Boolean: false < true.
@ -109,10 +109,10 @@ impl SimpleAggregationOp {
// String: lexicographic order. // String: lexicographic order.
String => Ok(the_type), String => Ok(the_type),
// These types are unordered. // Unordered types.
Keyword | Ref | Uuid => { Keyword | Ref | Uuid => {
bail!(ProjectorError::CannotApplyAggregateOperationToTypes( bail!(ProjectorError::CannotApplyAggregateOperationToTypes(
*self, self,
possibilities possibilities
)) ))
} }
@ -130,7 +130,7 @@ impl SimpleAggregationOp {
} }
} else { } else {
bail!(ProjectorError::CannotApplyAggregateOperationToTypes( bail!(ProjectorError::CannotApplyAggregateOperationToTypes(
*self, self,
possibilities possibilities
)) ))
} }

View file

@ -94,11 +94,11 @@ impl From<QueryOutput> for QueryResults {
impl QueryOutput { impl QueryOutput {
pub fn empty_factory(spec: &FindSpec) -> Box<dyn Fn() -> QueryResults> { pub fn empty_factory(spec: &FindSpec) -> Box<dyn Fn() -> QueryResults> {
use self::FindSpec::*; use self::FindSpec::*;
match spec { match *spec {
&FindScalar(_) => Box::new(|| QueryResults::Scalar(None)), FindScalar(_) => Box::new(|| QueryResults::Scalar(None)),
&FindTuple(_) => Box::new(|| QueryResults::Tuple(None)), FindTuple(_) => Box::new(|| QueryResults::Tuple(None)),
&FindColl(_) => Box::new(|| QueryResults::Coll(vec![])), FindColl(_) => Box::new(|| QueryResults::Coll(vec![])),
&FindRel(ref es) => { FindRel(ref es) => {
let width = es.len(); let width = es.len();
Box::new(move || QueryResults::Rel(RelResult::empty(width))) Box::new(move || QueryResults::Rel(RelResult::empty(width)))
} }
@ -115,48 +115,48 @@ impl QueryOutput {
pub fn empty(spec: &Rc<FindSpec>) -> QueryOutput { pub fn empty(spec: &Rc<FindSpec>) -> QueryOutput {
use self::FindSpec::*; use self::FindSpec::*;
let results = match &**spec { let results = match **spec {
&FindScalar(_) => QueryResults::Scalar(None), FindScalar(_) => QueryResults::Scalar(None),
&FindTuple(_) => QueryResults::Tuple(None), FindTuple(_) => QueryResults::Tuple(None),
&FindColl(_) => QueryResults::Coll(vec![]), FindColl(_) => QueryResults::Coll(vec![]),
&FindRel(ref es) => QueryResults::Rel(RelResult::empty(es.len())), FindRel(ref es) => QueryResults::Rel(RelResult::empty(es.len())),
}; };
QueryOutput { QueryOutput {
spec: spec.clone(), spec: spec.clone(),
results: results, results,
} }
} }
pub fn from_constants(spec: &Rc<FindSpec>, bindings: VariableBindings) -> QueryResults { pub fn from_constants(spec: &Rc<FindSpec>, bindings: VariableBindings) -> QueryResults {
use self::FindSpec::*; use self::FindSpec::*;
match &**spec { match **spec {
&FindScalar(Element::Variable(ref var)) FindScalar(Element::Variable(ref var))
| &FindScalar(Element::Corresponding(ref var)) => { | FindScalar(Element::Corresponding(ref var)) => {
let val = bindings.get(var).cloned().map(|v| v.into()); let val = bindings.get(var).cloned().map(|v| v.into());
QueryResults::Scalar(val) QueryResults::Scalar(val)
} }
&FindScalar(Element::Aggregate(ref _agg)) => { FindScalar(Element::Aggregate(ref _agg)) => {
// TODO: static aggregates. // TODO: static aggregates.
unimplemented!(); unimplemented!();
} }
&FindScalar(Element::Pull(ref _pull)) => { FindScalar(Element::Pull(ref _pull)) => {
// TODO: static pull. // TODO: static pull.
unimplemented!(); unimplemented!();
} }
&FindTuple(ref elements) => { FindTuple(ref elements) => {
let values = elements let values = elements
.iter() .iter()
.map(|e| match e { .map(|e| match *e {
&Element::Variable(ref var) | &Element::Corresponding(ref var) => bindings Element::Variable(ref var) | Element::Corresponding(ref var) => bindings
.get(var) .get(var)
.cloned() .cloned()
.expect("every var to have a binding") .expect("every var to have a binding")
.into(), .into(),
&Element::Pull(ref _pull) => { Element::Pull(ref _pull) => {
// TODO: static pull. // TODO: static pull.
unreachable!(); unreachable!();
} }
&Element::Aggregate(ref _agg) => { Element::Aggregate(ref _agg) => {
// TODO: static computation of aggregates, then // TODO: static computation of aggregates, then
// implement the condition in `is_fully_bound`. // implement the condition in `is_fully_bound`.
unreachable!(); unreachable!();
@ -165,7 +165,7 @@ impl QueryOutput {
.collect(); .collect();
QueryResults::Tuple(Some(values)) QueryResults::Tuple(Some(values))
} }
&FindColl(Element::Variable(ref var)) | &FindColl(Element::Corresponding(ref var)) => { FindColl(Element::Variable(ref var)) | FindColl(Element::Corresponding(ref var)) => {
let val = bindings let val = bindings
.get(var) .get(var)
.cloned() .cloned()
@ -173,32 +173,32 @@ impl QueryOutput {
.into(); .into();
QueryResults::Coll(vec![val]) QueryResults::Coll(vec![val])
} }
&FindColl(Element::Pull(ref _pull)) => { FindColl(Element::Pull(ref _pull)) => {
// TODO: static pull. // TODO: static pull.
unimplemented!(); unimplemented!();
} }
&FindColl(Element::Aggregate(ref _agg)) => { FindColl(Element::Aggregate(ref _agg)) => {
// Does it even make sense to write // Does it even make sense to write
// [:find [(max ?x) ...] :where [_ :foo/bar ?x]] // [:find [(max ?x) ...] :where [_ :foo/bar ?x]]
// ? // ?
// TODO // TODO
unimplemented!(); unimplemented!();
} }
&FindRel(ref elements) => { FindRel(ref elements) => {
let width = elements.len(); let width = elements.len();
let values = elements let values = elements
.iter() .iter()
.map(|e| match e { .map(|e| match *e {
&Element::Variable(ref var) | &Element::Corresponding(ref var) => bindings Element::Variable(ref var) | Element::Corresponding(ref var) => bindings
.get(var) .get(var)
.cloned() .cloned()
.expect("every var to have a binding") .expect("every var to have a binding")
.into(), .into(),
&Element::Pull(ref _pull) => { Element::Pull(ref _pull) => {
// TODO: static pull. // TODO: static pull.
unreachable!(); unreachable!();
} }
&Element::Aggregate(ref _agg) => { Element::Aggregate(ref _agg) => {
// TODO: static computation of aggregates, then // TODO: static computation of aggregates, then
// implement the condition in `is_fully_bound`. // implement the condition in `is_fully_bound`.
unreachable!(); unreachable!();
@ -242,33 +242,33 @@ impl QueryOutput {
impl QueryResults { impl QueryResults {
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
use QueryResults::*; use QueryResults::*;
match self { match *self {
&Scalar(ref o) => { Scalar(ref o) => {
if o.is_some() { if o.is_some() {
1 1
} else { } else {
0 0
} }
} }
&Tuple(ref o) => { Tuple(ref o) => {
if o.is_some() { if o.is_some() {
1 1
} else { } else {
0 0
} }
} }
&Coll(ref v) => v.len(), Coll(ref v) => v.len(),
&Rel(ref r) => r.row_count(), Rel(ref r) => r.row_count(),
} }
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
use QueryResults::*; use QueryResults::*;
match self { match *self {
&Scalar(ref o) => o.is_none(), Scalar(ref o) => o.is_none(),
&Tuple(ref o) => o.is_none(), Tuple(ref o) => o.is_none(),
&Coll(ref v) => v.is_empty(), Coll(ref v) => v.is_empty(),
&Rel(ref r) => r.is_empty(), Rel(ref r) => r.is_empty(),
} }
} }
@ -341,14 +341,14 @@ impl TypedIndex {
fn lookup<'a>(&self, row: &Row<'a>) -> Result<Binding> { fn lookup<'a>(&self, row: &Row<'a>) -> Result<Binding> {
use TypedIndex::*; use TypedIndex::*;
match self { match *self {
&Known(value_index, value_type) => { Known(value_index, value_type) => {
let v: rusqlite::types::Value = row.get(value_index).unwrap(); let v: rusqlite::types::Value = row.get(value_index).unwrap();
TypedValue::from_sql_value_pair(v, value_type) TypedValue::from_sql_value_pair(v, value_type)
.map(|v| v.into()) .map(|v| v.into())
.map_err(|e| e.into()) .map_err(|e| e.into())
} }
&Unknown(value_index, type_index) => { Unknown(value_index, type_index) => {
let v: rusqlite::types::Value = row.get(value_index).unwrap(); let v: rusqlite::types::Value = row.get(value_index).unwrap();
let value_type_tag: i32 = row.get(type_index).unwrap(); let value_type_tag: i32 = row.get(type_index).unwrap();
TypedValue::from_sql_value_pair(v, value_type_tag) TypedValue::from_sql_value_pair(v, value_type_tag)
@ -403,8 +403,8 @@ trait IsPull {
impl IsPull for Element { impl IsPull for Element {
fn is_pull(&self) -> bool { fn is_pull(&self) -> bool {
match self { match *self {
&Element::Pull(_) => true, Element::Pull(_) => true,
_ => false, _ => false,
} }
} }
@ -430,16 +430,16 @@ pub fn query_projection(
let variables: BTreeSet<Variable> = spec let variables: BTreeSet<Variable> = spec
.columns() .columns()
.map(|e| match e { .map(|e| match *e {
&Element::Variable(ref var) | &Element::Corresponding(ref var) => var.clone(), Element::Variable(ref var) | Element::Corresponding(ref var) => var.clone(),
// Pull expressions can never be fully bound. // Pull expressions can never be fully bound.
// TODO: but the interior can be, in which case we // TODO: but the interior can be, in which case we
// can handle this and simply project. // can handle this and simply project.
&Element::Pull(_) => { Element::Pull(_) => {
unreachable!(); unreachable!();
} }
&Element::Aggregate(ref _agg) => { Element::Aggregate(ref _agg) => {
// TODO: static computation of aggregates, then // TODO: static computation of aggregates, then
// implement the condition in `is_fully_bound`. // implement the condition in `is_fully_bound`.
unreachable!(); unreachable!();

View file

@ -67,7 +67,7 @@ impl ProjectedElements {
sql_projection: self.sql_projection, sql_projection: self.sql_projection,
pre_aggregate_projection: self.pre_aggregate_projection, pre_aggregate_projection: self.pre_aggregate_projection,
datalog_projector: projector, datalog_projector: projector,
distinct: distinct, distinct,
group_by_cols: self.group_by, group_by_cols: self.group_by,
}) })
} }
@ -98,14 +98,14 @@ fn candidate_type_column(
let type_name = VariableColumn::VariableTypeTag(var.clone()).column_name(); let type_name = VariableColumn::VariableTypeTag(var.clone()).column_name();
(ColumnOrExpression::Column(alias), type_name) (ColumnOrExpression::Column(alias), type_name)
}) })
.ok_or_else(|| ProjectorError::UnboundVariable(var.name()).into()) .ok_or_else(|| ProjectorError::UnboundVariable(var.name()))
} }
fn cc_column(cc: &ConjoiningClauses, var: &Variable) -> Result<QualifiedAlias> { fn cc_column(cc: &ConjoiningClauses, var: &Variable) -> Result<QualifiedAlias> {
cc.column_bindings cc.column_bindings
.get(var) .get(var)
.and_then(|cols| cols.get(0).cloned()) .and_then(|cols| cols.get(0).cloned())
.ok_or_else(|| ProjectorError::UnboundVariable(var.name()).into()) .ok_or_else(|| ProjectorError::UnboundVariable(var.name()))
} }
fn candidate_column(cc: &ConjoiningClauses, var: &Variable) -> Result<(ColumnOrExpression, Name)> { fn candidate_column(cc: &ConjoiningClauses, var: &Variable) -> Result<(ColumnOrExpression, Name)> {
@ -130,7 +130,7 @@ pub fn projected_column_for_var(
let tag = value.value_type(); let tag = value.value_type();
let name = VariableColumn::Variable(var.clone()).column_name(); let name = VariableColumn::Variable(var.clone()).column_name();
Ok(( Ok((
ProjectedColumn(ColumnOrExpression::Value(value.clone()), name), ProjectedColumn(ColumnOrExpression::Value(value), name),
ValueTypeSet::of_one(tag), ValueTypeSet::of_one(tag),
)) ))
} else { } else {
@ -184,8 +184,8 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
for e in elements { for e in elements {
// Check for and reject duplicates. // Check for and reject duplicates.
match e { match *e {
&Element::Variable(ref var) => { Element::Variable(ref var) => {
if outer_variables.contains(var) { if outer_variables.contains(var) {
bail!(ProjectorError::InvalidProjection(format!( bail!(ProjectorError::InvalidProjection(format!(
"Duplicate variable {} in query.", "Duplicate variable {} in query.",
@ -199,7 +199,7 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
))); )));
} }
} }
&Element::Corresponding(ref var) => { Element::Corresponding(ref var) => {
if outer_variables.contains(var) { if outer_variables.contains(var) {
bail!(ProjectorError::InvalidProjection(format!( bail!(ProjectorError::InvalidProjection(format!(
"Can't project both {} and `(the {})` from a query.", "Can't project both {} and `(the {})` from a query.",
@ -213,38 +213,35 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
))); )));
} }
} }
&Element::Aggregate(_) => {} Element::Aggregate(_) => {}
&Element::Pull(_) => {} Element::Pull(_) => {}
}; };
// Record variables -- `(the ?x)` and `?x` are different in this regard, because we don't want // Record variables -- `(the ?x)` and `?x` are different in this regard, because we don't want
// to group on variables that are corresponding-projected. // to group on variables that are corresponding-projected.
match e { match *e {
&Element::Variable(ref var) => { Element::Variable(ref var) => {
outer_variables.insert(var.clone()); outer_variables.insert(var.clone());
} }
&Element::Corresponding(ref var) => { Element::Corresponding(ref var) => {
// We will project these later; don't put them in `outer_variables` // We will project these later; don't put them in `outer_variables`
// so we know not to group them. // so we know not to group them.
corresponded_variables.insert(var.clone()); corresponded_variables.insert(var.clone());
} }
&Element::Pull(Pull { Element::Pull(Pull { ref var, .. }) => {
ref var,
patterns: _,
}) => {
// We treat `pull` as an ordinary variable extraction, // We treat `pull` as an ordinary variable extraction,
// and we expand it later. // and we expand it later.
outer_variables.insert(var.clone()); outer_variables.insert(var.clone());
} }
&Element::Aggregate(_) => {} Element::Aggregate(_) => {}
}; };
// Now do the main processing of each element. // Now do the main processing of each element.
match e { match *e {
// Each time we come across a variable, we push a SQL column // Each time we come across a variable, we push a SQL column
// into the SQL projection, aliased to the name of the variable, // into the SQL projection, aliased to the name of the variable,
// and we push an annotated index into the projector. // and we push an annotated index into the projector.
&Element::Variable(ref var) | &Element::Corresponding(ref var) => { Element::Variable(ref var) | Element::Corresponding(ref var) => {
inner_variables.insert(var.clone()); inner_variables.insert(var.clone());
let (projected_column, type_set) = projected_column_for_var(&var, &query.cc)?; let (projected_column, type_set) = projected_column_for_var(&var, &query.cc)?;
@ -264,7 +261,7 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
outer_projection.push(Either::Left(type_name)); outer_projection.push(Either::Left(type_name));
} }
} }
&Element::Pull(Pull { Element::Pull(Pull {
ref var, ref var,
ref patterns, ref patterns,
}) => { }) => {
@ -296,7 +293,7 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
unreachable!(); unreachable!();
} }
} }
&Element::Aggregate(ref a) => { Element::Aggregate(ref a) => {
if let Some(simple) = a.to_simple() { if let Some(simple) = a.to_simple() {
aggregates = true; aggregates = true;
@ -343,7 +340,7 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
templates.push(TypedIndex::Known(i, return_type.value_type_tag())); templates.push(TypedIndex::Known(i, return_type.value_type_tag()));
i += 1; i += 1;
} else { } else {
// TODO: complex aggregates. // TODO(gburd): complex aggregates.
bail!(ProjectorError::NotYetImplemented( bail!(ProjectorError::NotYetImplemented(
"complex aggregates".into() "complex aggregates".into()
)); ));
@ -465,9 +462,12 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
if needs_type_projection { if needs_type_projection {
let type_name = VariableColumn::VariableTypeTag(var.clone()).column_name(); let type_name = VariableColumn::VariableTypeTag(var.clone()).column_name();
if !already_inner { if !already_inner {
let type_col = query.cc.extracted_types.get(&var).cloned().ok_or_else(|| { let type_col = query
ProjectorError::NoTypeAvailableForVariable(var.name().clone()) .cc
})?; .extracted_types
.get(&var)
.cloned()
.ok_or_else(|| ProjectorError::NoTypeAvailableForVariable(var.name()))?;
inner_projection.push(ProjectedColumn( inner_projection.push(ProjectedColumn(
ColumnOrExpression::Column(type_col), ColumnOrExpression::Column(type_col),
type_name.clone(), type_name.clone(),

View file

@ -29,18 +29,15 @@ impl ConstantProjector {
results_factory: Box<dyn Fn() -> QueryResults>, results_factory: Box<dyn Fn() -> QueryResults>,
) -> ConstantProjector { ) -> ConstantProjector {
ConstantProjector { ConstantProjector {
spec: spec, spec,
results_factory: results_factory, results_factory,
} }
} }
pub fn project_without_rows<'stmt>(&self) -> Result<QueryOutput> { pub fn project_without_rows(&self) -> Result<QueryOutput> {
let results = (self.results_factory)(); let results = (self.results_factory)();
let spec = self.spec.clone(); let spec = self.spec.clone();
Ok(QueryOutput { Ok(QueryOutput { spec, results })
spec: spec,
results: results,
})
} }
} }

View file

@ -42,8 +42,8 @@ impl ScalarTwoStagePullProjector {
pull: PullOperation, pull: PullOperation,
) -> Result<ScalarTwoStagePullProjector> { ) -> Result<ScalarTwoStagePullProjector> {
Ok(ScalarTwoStagePullProjector { Ok(ScalarTwoStagePullProjector {
spec: spec, spec,
puller: Puller::prepare(schema, pull.0.clone())?, puller: Puller::prepare(schema, pull.0)?,
}) })
} }
@ -86,7 +86,7 @@ impl Projector for ScalarTwoStagePullProjector {
Ok(QueryOutput { Ok(QueryOutput {
spec: self.spec.clone(), spec: self.spec.clone(),
results: results, results,
}) })
} }
@ -111,10 +111,10 @@ impl TupleTwoStagePullProjector {
pulls: Vec<PullTemplate>, pulls: Vec<PullTemplate>,
) -> TupleTwoStagePullProjector { ) -> TupleTwoStagePullProjector {
TupleTwoStagePullProjector { TupleTwoStagePullProjector {
spec: spec, spec,
len: len, len,
templates: templates, templates,
pulls: pulls, pulls,
} }
} }
@ -187,7 +187,7 @@ impl Projector for TupleTwoStagePullProjector {
}; };
Ok(QueryOutput { Ok(QueryOutput {
spec: self.spec.clone(), spec: self.spec.clone(),
results: results, results,
}) })
} }
@ -215,10 +215,10 @@ impl RelTwoStagePullProjector {
pulls: Vec<PullTemplate>, pulls: Vec<PullTemplate>,
) -> RelTwoStagePullProjector { ) -> RelTwoStagePullProjector {
RelTwoStagePullProjector { RelTwoStagePullProjector {
spec: spec, spec,
len: len, len,
templates: templates, templates,
pulls: pulls, pulls,
} }
} }
@ -320,10 +320,7 @@ pub(crate) struct CollTwoStagePullProjector {
impl CollTwoStagePullProjector { impl CollTwoStagePullProjector {
fn with_pull(spec: Rc<FindSpec>, pull: PullOperation) -> CollTwoStagePullProjector { fn with_pull(spec: Rc<FindSpec>, pull: PullOperation) -> CollTwoStagePullProjector {
CollTwoStagePullProjector { CollTwoStagePullProjector { spec, pull }
spec: spec,
pull: pull,
}
} }
pub(crate) fn combine( pub(crate) fn combine(

View file

@ -26,10 +26,7 @@ pub(crate) struct ScalarProjector {
impl ScalarProjector { impl ScalarProjector {
fn with_template(spec: Rc<FindSpec>, template: TypedIndex) -> ScalarProjector { fn with_template(spec: Rc<FindSpec>, template: TypedIndex) -> ScalarProjector {
ScalarProjector { ScalarProjector { spec, template }
spec: spec,
template: template,
}
} }
pub(crate) fn combine( pub(crate) fn combine(
@ -62,7 +59,7 @@ impl Projector for ScalarProjector {
}; };
Ok(QueryOutput { Ok(QueryOutput {
spec: self.spec.clone(), spec: self.spec.clone(),
results: results, results,
}) })
} }
@ -85,9 +82,9 @@ impl TupleProjector {
templates: Vec<TypedIndex>, templates: Vec<TypedIndex>,
) -> TupleProjector { ) -> TupleProjector {
TupleProjector { TupleProjector {
spec: spec, spec,
len: len, len,
templates: templates, templates,
} }
} }
@ -134,7 +131,7 @@ impl Projector for TupleProjector {
}; };
Ok(QueryOutput { Ok(QueryOutput {
spec: self.spec.clone(), spec: self.spec.clone(),
results: results, results,
}) })
} }
@ -156,9 +153,9 @@ pub(crate) struct RelProjector {
impl RelProjector { impl RelProjector {
fn with_templates(spec: Rc<FindSpec>, len: usize, templates: Vec<TypedIndex>) -> RelProjector { fn with_templates(spec: Rc<FindSpec>, len: usize, templates: Vec<TypedIndex>) -> RelProjector {
RelProjector { RelProjector {
spec: spec, spec,
len: len, len,
templates: templates, templates,
} }
} }
@ -235,10 +232,7 @@ pub(crate) struct CollProjector {
impl CollProjector { impl CollProjector {
fn with_template(spec: Rc<FindSpec>, template: TypedIndex) -> CollProjector { fn with_template(spec: Rc<FindSpec>, template: TypedIndex) -> CollProjector {
CollProjector { CollProjector { spec, template }
spec: spec,
template: template,
}
} }
pub(crate) fn combine( pub(crate) fn combine(

View file

@ -61,9 +61,9 @@ impl<'schema> PullConsumer<'schema> {
indices: PullIndices, indices: PullIndices,
) -> PullConsumer<'schema> { ) -> PullConsumer<'schema> {
PullConsumer { PullConsumer {
indices: indices, indices,
schema: schema, schema,
puller: puller, puller,
entities: Default::default(), entities: Default::default(),
results: Default::default(), results: Default::default(),
} }
@ -114,10 +114,6 @@ impl<'schema> PullConsumer<'schema> {
// TODO: do we need to include empty maps for entities that didn't match any pull? // TODO: do we need to include empty maps for entities that didn't match any pull?
pub(crate) fn into_coll_results(self) -> Vec<Binding> { pub(crate) fn into_coll_results(self) -> Vec<Binding> {
self.results self.results.values().cloned().map(Binding::Map).collect()
.values()
.cloned()
.map(|vrc| Binding::Map(vrc))
.collect()
} }
} }

View file

@ -39,7 +39,7 @@ pub type StructuredRelResult = RelResult<Binding>;
impl<T> RelResult<T> { impl<T> RelResult<T> {
pub fn empty(width: usize) -> RelResult<T> { pub fn empty(width: usize) -> RelResult<T> {
RelResult { RelResult {
width: width, width,
values: Vec::new(), values: Vec::new(),
} }
} }
@ -53,7 +53,7 @@ impl<T> RelResult<T> {
} }
pub fn rows(&self) -> ::std::slice::Chunks<T> { pub fn rows(&self) -> ::std::slice::Chunks<T> {
// TODO: Nightly-only API `exact_chunks`. #47115. // TODO(gburd): Nightly-only API `exact_chunks`. #47115.
self.values.chunks(self.width) self.values.chunks(self.width)
} }
@ -142,7 +142,7 @@ impl From<Vec<Vec<TypedValue>>> for RelResult<Binding> {
} else { } else {
let width = src.get(0).map(|r| r.len()).unwrap_or(0); let width = src.get(0).map(|r| r.len()).unwrap_or(0);
RelResult { RelResult {
width: width, width,
values: src values: src
.into_iter() .into_iter()
.flat_map(|r| r.into_iter().map(|v| v.into())) .flat_map(|r| r.into_iter().map(|v| v.into()))

View file

@ -224,7 +224,7 @@ impl ToConstraint for ColumnConstraint {
NotExists(computed_table) => { NotExists(computed_table) => {
let subquery = table_for_computed(computed_table, TableAlias::new()); let subquery = table_for_computed(computed_table, TableAlias::new());
Constraint::NotExists { subquery: subquery } Constraint::NotExists { subquery }
} }
} }
} }
@ -246,7 +246,7 @@ struct ConsumableVec<T> {
impl<T> From<Vec<T>> for ConsumableVec<T> { impl<T> From<Vec<T>> for ConsumableVec<T> {
fn from(vec: Vec<T>) -> ConsumableVec<T> { fn from(vec: Vec<T>) -> ConsumableVec<T> {
ConsumableVec { ConsumableVec {
inner: vec.into_iter().map(|x| Some(x)).collect(), inner: vec.into_iter().map(Some).collect(),
} }
} }
} }
@ -369,20 +369,20 @@ fn cc_to_select_query(
FromClause::TableList(TableList(tables.collect())) FromClause::TableList(TableList(tables.collect()))
}; };
let order = order.map_or(vec![], |vec| vec.into_iter().map(|o| o.into()).collect()); let order = order.map_or(vec![], |vec| vec.into_iter().map(|o| o).collect());
let limit = if cc.empty_because.is_some() { let limit = if cc.empty_because.is_some() {
Limit::Fixed(0) Limit::Fixed(0)
} else { } else {
limit limit
}; };
SelectQuery { SelectQuery {
distinct: distinct, distinct,
projection: projection, projection,
from: from, from,
group_by: group_by, group_by,
constraints: cc.wheres.into_iter().map(|c| c.to_constraint()).collect(), constraints: cc.wheres.into_iter().map(|c| c.to_constraint()).collect(),
order: order, order,
limit: limit, limit,
} }
} }
@ -412,11 +412,11 @@ fn re_project(mut inner: SelectQuery, projection: Projection) -> SelectQuery {
use self::Projection::*; use self::Projection::*;
let nullable = match &projection { let nullable = match projection {
&Columns(ref columns) => columns Columns(ref columns) => columns
.iter() .iter()
.filter_map(|pc| match pc { .filter_map(|pc| match *pc {
&ProjectedColumn(ColumnOrExpression::NullableAggregate(_, _), ref name) => { ProjectedColumn(ColumnOrExpression::NullableAggregate(_, _), ref name) => {
Some(Constraint::IsNotNull { Some(Constraint::IsNotNull {
value: ColumnOrExpression::ExistingColumn(name.clone()), value: ColumnOrExpression::ExistingColumn(name.clone()),
}) })
@ -424,21 +424,21 @@ fn re_project(mut inner: SelectQuery, projection: Projection) -> SelectQuery {
_ => None, _ => None,
}) })
.collect(), .collect(),
&Star => vec![], Star => vec![],
&One => vec![], One => vec![],
}; };
if nullable.is_empty() { if nullable.is_empty() {
return SelectQuery { return SelectQuery {
distinct: outer_distinct, distinct: outer_distinct,
projection: projection, projection,
from: FromClause::TableList(TableList(vec![TableOrSubquery::Subquery(Box::new( from: FromClause::TableList(TableList(vec![TableOrSubquery::Subquery(Box::new(
inner, inner,
))])), ))])),
constraints: vec![], constraints: vec![],
group_by: group_by, group_by,
order: order_by, order: order_by,
limit: limit, limit,
}; };
} }
@ -449,10 +449,10 @@ fn re_project(mut inner: SelectQuery, projection: Projection) -> SelectQuery {
// if there is. // if there is.
let subselect = SelectQuery { let subselect = SelectQuery {
distinct: outer_distinct, distinct: outer_distinct,
projection: projection, projection,
from: FromClause::TableList(TableList(vec![TableOrSubquery::Subquery(Box::new(inner))])), from: FromClause::TableList(TableList(vec![TableOrSubquery::Subquery(Box::new(inner))])),
constraints: vec![], constraints: vec![],
group_by: group_by, group_by,
order: match &limit { order: match &limit {
&Limit::None => vec![], &Limit::None => vec![],
&Limit::Fixed(_) | &Limit::Variable(_) => order_by.clone(), &Limit::Fixed(_) | &Limit::Variable(_) => order_by.clone(),
@ -499,8 +499,7 @@ pub fn query_to_select(schema: &Schema, query: AlgebraicQuery) -> Result<Project
query.order, query.order,
query.limit, query.limit,
); );
let outer = re_project(inner, sql_projection); re_project(inner, sql_projection) // outer
outer
} }
None => cc_to_select_query( None => cc_to_select_query(
sql_projection, sql_projection,