lint
This commit is contained in:
parent
58e06742fd
commit
dfb5866174
9 changed files with 146 additions and 163 deletions
|
@ -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
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!();
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()))
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue