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

View file

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

View file

@ -67,7 +67,7 @@ impl ProjectedElements {
sql_projection: self.sql_projection,
pre_aggregate_projection: self.pre_aggregate_projection,
datalog_projector: projector,
distinct: distinct,
distinct,
group_by_cols: self.group_by,
})
}
@ -98,14 +98,14 @@ fn candidate_type_column(
let type_name = VariableColumn::VariableTypeTag(var.clone()).column_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> {
cc.column_bindings
.get(var)
.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)> {
@ -130,7 +130,7 @@ pub fn projected_column_for_var(
let tag = value.value_type();
let name = VariableColumn::Variable(var.clone()).column_name();
Ok((
ProjectedColumn(ColumnOrExpression::Value(value.clone()), name),
ProjectedColumn(ColumnOrExpression::Value(value), name),
ValueTypeSet::of_one(tag),
))
} else {
@ -184,8 +184,8 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
for e in elements {
// Check for and reject duplicates.
match e {
&Element::Variable(ref var) => {
match *e {
Element::Variable(ref var) => {
if outer_variables.contains(var) {
bail!(ProjectorError::InvalidProjection(format!(
"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) {
bail!(ProjectorError::InvalidProjection(format!(
"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::Pull(_) => {}
Element::Aggregate(_) => {}
Element::Pull(_) => {}
};
// Record variables -- `(the ?x)` and `?x` are different in this regard, because we don't want
// to group on variables that are corresponding-projected.
match e {
&Element::Variable(ref var) => {
match *e {
Element::Variable(ref var) => {
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`
// so we know not to group them.
corresponded_variables.insert(var.clone());
}
&Element::Pull(Pull {
ref var,
patterns: _,
}) => {
Element::Pull(Pull { ref var, .. }) => {
// We treat `pull` as an ordinary variable extraction,
// and we expand it later.
outer_variables.insert(var.clone());
}
&Element::Aggregate(_) => {}
Element::Aggregate(_) => {}
};
// Now do the main processing of each element.
match e {
match *e {
// Each time we come across a variable, we push a SQL column
// into the SQL projection, aliased to the name of the variable,
// 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());
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));
}
}
&Element::Pull(Pull {
Element::Pull(Pull {
ref var,
ref patterns,
}) => {
@ -296,7 +293,7 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
unreachable!();
}
}
&Element::Aggregate(ref a) => {
Element::Aggregate(ref a) => {
if let Some(simple) = a.to_simple() {
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()));
i += 1;
} else {
// TODO: complex aggregates.
// TODO(gburd): complex aggregates.
bail!(ProjectorError::NotYetImplemented(
"complex aggregates".into()
));
@ -465,9 +462,12 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
if needs_type_projection {
let type_name = VariableColumn::VariableTypeTag(var.clone()).column_name();
if !already_inner {
let type_col = query.cc.extracted_types.get(&var).cloned().ok_or_else(|| {
ProjectorError::NoTypeAvailableForVariable(var.name().clone())
})?;
let type_col = query
.cc
.extracted_types
.get(&var)
.cloned()
.ok_or_else(|| ProjectorError::NoTypeAvailableForVariable(var.name()))?;
inner_projection.push(ProjectedColumn(
ColumnOrExpression::Column(type_col),
type_name.clone(),

View file

@ -29,18 +29,15 @@ impl ConstantProjector {
results_factory: Box<dyn Fn() -> QueryResults>,
) -> ConstantProjector {
ConstantProjector {
spec: spec,
results_factory: results_factory,
spec,
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 spec = self.spec.clone();
Ok(QueryOutput {
spec: spec,
results: results,
})
Ok(QueryOutput { spec, results })
}
}

View file

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

View file

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

View file

@ -61,9 +61,9 @@ impl<'schema> PullConsumer<'schema> {
indices: PullIndices,
) -> PullConsumer<'schema> {
PullConsumer {
indices: indices,
schema: schema,
puller: puller,
indices,
schema,
puller,
entities: 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?
pub(crate) fn into_coll_results(self) -> Vec<Binding> {
self.results
.values()
.cloned()
.map(|vrc| Binding::Map(vrc))
.collect()
self.results.values().cloned().map(Binding::Map).collect()
}
}

View file

@ -39,7 +39,7 @@ pub type StructuredRelResult = RelResult<Binding>;
impl<T> RelResult<T> {
pub fn empty(width: usize) -> RelResult<T> {
RelResult {
width: width,
width,
values: Vec::new(),
}
}
@ -53,7 +53,7 @@ impl<T> RelResult<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)
}
@ -142,7 +142,7 @@ impl From<Vec<Vec<TypedValue>>> for RelResult<Binding> {
} else {
let width = src.get(0).map(|r| r.len()).unwrap_or(0);
RelResult {
width: width,
width,
values: src
.into_iter()
.flat_map(|r| r.into_iter().map(|v| v.into()))

View file

@ -224,7 +224,7 @@ impl ToConstraint for ColumnConstraint {
NotExists(computed_table) => {
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> {
fn from(vec: Vec<T>) -> ConsumableVec<T> {
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()))
};
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() {
Limit::Fixed(0)
} else {
limit
};
SelectQuery {
distinct: distinct,
projection: projection,
from: from,
group_by: group_by,
distinct,
projection,
from,
group_by,
constraints: cc.wheres.into_iter().map(|c| c.to_constraint()).collect(),
order: order,
limit: limit,
order,
limit,
}
}
@ -412,11 +412,11 @@ fn re_project(mut inner: SelectQuery, projection: Projection) -> SelectQuery {
use self::Projection::*;
let nullable = match &projection {
&Columns(ref columns) => columns
let nullable = match projection {
Columns(ref columns) => columns
.iter()
.filter_map(|pc| match pc {
&ProjectedColumn(ColumnOrExpression::NullableAggregate(_, _), ref name) => {
.filter_map(|pc| match *pc {
ProjectedColumn(ColumnOrExpression::NullableAggregate(_, _), ref name) => {
Some(Constraint::IsNotNull {
value: ColumnOrExpression::ExistingColumn(name.clone()),
})
@ -424,21 +424,21 @@ fn re_project(mut inner: SelectQuery, projection: Projection) -> SelectQuery {
_ => None,
})
.collect(),
&Star => vec![],
&One => vec![],
Star => vec![],
One => vec![],
};
if nullable.is_empty() {
return SelectQuery {
distinct: outer_distinct,
projection: projection,
projection,
from: FromClause::TableList(TableList(vec![TableOrSubquery::Subquery(Box::new(
inner,
))])),
constraints: vec![],
group_by: group_by,
group_by,
order: order_by,
limit: limit,
limit,
};
}
@ -449,10 +449,10 @@ fn re_project(mut inner: SelectQuery, projection: Projection) -> SelectQuery {
// if there is.
let subselect = SelectQuery {
distinct: outer_distinct,
projection: projection,
projection,
from: FromClause::TableList(TableList(vec![TableOrSubquery::Subquery(Box::new(inner))])),
constraints: vec![],
group_by: group_by,
group_by,
order: match &limit {
&Limit::None => vec![],
&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.limit,
);
let outer = re_project(inner, sql_projection);
outer
re_project(inner, sql_projection) // outer
}
None => cc_to_select_query(
sql_projection,