macro_rules! bail {
($e:expr) => {
return ::std::result::Result::Err(::std::convert::From::from($e))
};
}
extern crate indexmap;
extern crate mentat_sql;
extern crate rusqlite;
extern crate core_traits;
extern crate db_traits;
extern crate edn;
extern crate mentat_core;
extern crate mentat_db; extern crate mentat_query_algebrizer;
extern crate mentat_query_pull;
extern crate mentat_query_sql;
extern crate query_projector_traits;
extern crate query_pull_traits;
use std::collections::BTreeSet;
use std::iter;
use std::rc::Rc;
pub use mentat_sql::{SqlConn, SqlRow, SqlValue};
pub type Row<'a> = dyn SqlRow + 'a;
pub trait RowSource {
fn next_row(&mut self) -> Result<Option<&dyn SqlRow>>;
}
pub struct VecRows {
rows: std::vec::IntoIter<Vec<SqlValue>>,
current: Option<Vec<SqlValue>>,
}
impl VecRows {
pub fn new(rows: Vec<Vec<SqlValue>>) -> Self {
VecRows {
rows: rows.into_iter(),
current: None,
}
}
}
impl RowSource for VecRows {
fn next_row(&mut self) -> Result<Option<&dyn SqlRow>> {
self.current = self.rows.next();
Ok(self.current.as_ref().map(|r| r as &dyn SqlRow))
}
}
pub struct RusqliteRows<'stmt> {
rows: rusqlite::Rows<'stmt>,
current: Option<Vec<SqlValue>>,
}
impl<'stmt> RusqliteRows<'stmt> {
pub fn new(rows: rusqlite::Rows<'stmt>) -> Self {
RusqliteRows {
rows,
current: None,
}
}
}
impl RowSource for RusqliteRows<'_> {
fn next_row(&mut self) -> Result<Option<&dyn SqlRow>> {
match self.rows.next()? {
Some(row) => {
let n = row.as_ref().column_count();
let mut vals = Vec::with_capacity(n);
for i in 0..n {
let v: rusqlite::types::Value = row.get(i)?;
vals.push(mentat_sql::from_rusqlite(v));
}
self.current = Some(vals);
}
None => self.current = None,
}
Ok(self.current.as_ref().map(|r| r as &dyn SqlRow))
}
}
use core_traits::{Binding, TypedValue};
use mentat_core::{Schema, ValueTypeTag};
use mentat_core::util::Either;
use mentat_db::TypedSQLValue;
use edn::query::{Element, FindSpec, Limit, Variable};
use mentat_query_algebrizer::{AlgebraicQuery, VariableBindings};
use mentat_query_sql::{GroupBy, Projection};
pub mod translate;
mod binding_tuple;
pub use crate::binding_tuple::BindingTuple;
mod project;
mod projectors;
mod pull;
mod relresult;
use crate::project::{project_elements, ProjectedElements};
pub use crate::project::projected_column_for_var;
pub use crate::projectors::{ConstantProjector, Projector};
use crate::projectors::{
CollProjector, CollTwoStagePullProjector, RelProjector, RelTwoStagePullProjector,
ScalarProjector, ScalarTwoStagePullProjector, TupleProjector, TupleTwoStagePullProjector,
};
pub use crate::relresult::{RelResult, StructuredRelResult};
use query_projector_traits::errors::{ProjectorError, Result};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QueryOutput {
pub spec: Rc<FindSpec>,
pub results: QueryResults,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum QueryResults {
Scalar(Option<Binding>),
Tuple(Option<Vec<Binding>>),
Coll(Vec<Binding>),
Rel(RelResult<Binding>),
}
impl From<QueryOutput> for QueryResults {
fn from(o: QueryOutput) -> QueryResults {
o.results
}
}
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) => {
let width = es.len();
Box::new(move || QueryResults::Rel(RelResult::empty(width)))
}
}
}
pub fn len(&self) -> usize {
self.results.len()
}
pub fn is_empty(&self) -> bool {
self.results.is_empty()
}
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())),
};
QueryOutput {
spec: spec.clone(),
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)) => {
let val = bindings.get(var).cloned().map(|v| v.into());
QueryResults::Scalar(val)
}
FindScalar(Element::Aggregate(ref _agg)) => {
unreachable!("aggregate in a fully-bound scalar find: rejected by is_fully_bound")
}
FindScalar(Element::Pull(ref _pull)) => {
unreachable!("pull in a fully-bound scalar find: rejected by is_fully_bound")
}
FindTuple(ref elements) => {
let values = elements
.iter()
.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) => {
unreachable!();
}
Element::Aggregate(ref _agg) => {
unreachable!();
}
})
.collect();
QueryResults::Tuple(Some(values))
}
FindColl(Element::Variable(ref var)) | FindColl(Element::Corresponding(ref var)) => {
let val = bindings
.get(var)
.cloned()
.expect("every var to have a binding")
.into();
QueryResults::Coll(vec![val])
}
FindColl(Element::Pull(ref _pull)) => {
unreachable!("pull in a fully-bound coll find: rejected by is_fully_bound")
}
FindColl(Element::Aggregate(ref _agg)) => {
unreachable!("aggregate in a fully-bound coll find: rejected by is_fully_bound")
}
FindRel(ref elements) => {
let width = elements.len();
let values = elements
.iter()
.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) => {
unreachable!();
}
Element::Aggregate(ref _agg) => {
unreachable!();
}
})
.collect();
QueryResults::Rel(RelResult { width, values })
}
}
}
pub fn into_scalar(self) -> Result<Option<Binding>> {
self.results.into_scalar()
}
pub fn into_coll(self) -> Result<Vec<Binding>> {
self.results.into_coll()
}
pub fn into_tuple<B>(self) -> Result<Option<B>>
where
B: BindingTuple,
{
let expected = self.spec.expected_column_count();
self.results
.into_tuple()
.and_then(|vec| B::from_binding_vec(expected, vec))
}
pub fn into_rel(self) -> Result<RelResult<Binding>> {
self.results.into_rel()
}
}
impl QueryResults {
pub fn len(&self) -> usize {
use crate::QueryResults::*;
match *self {
Scalar(ref o) => {
if o.is_some() {
1
} else {
0
}
}
Tuple(ref o) => {
if o.is_some() {
1
} else {
0
}
}
Coll(ref v) => v.len(),
Rel(ref r) => r.row_count(),
}
}
pub fn is_empty(&self) -> bool {
use crate::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(),
}
}
pub fn into_scalar(self) -> Result<Option<Binding>> {
match self {
QueryResults::Scalar(o) => Ok(o),
QueryResults::Coll(_) => bail!(ProjectorError::UnexpectedResultsType("coll", "scalar")),
QueryResults::Tuple(_) => {
bail!(ProjectorError::UnexpectedResultsType("tuple", "scalar"))
}
QueryResults::Rel(_) => bail!(ProjectorError::UnexpectedResultsType("rel", "scalar")),
}
}
pub fn into_coll(self) -> Result<Vec<Binding>> {
match self {
QueryResults::Scalar(_) => {
bail!(ProjectorError::UnexpectedResultsType("scalar", "coll"))
}
QueryResults::Coll(c) => Ok(c),
QueryResults::Tuple(_) => bail!(ProjectorError::UnexpectedResultsType("tuple", "coll")),
QueryResults::Rel(_) => bail!(ProjectorError::UnexpectedResultsType("rel", "coll")),
}
}
pub fn into_tuple(self) -> Result<Option<Vec<Binding>>> {
match self {
QueryResults::Scalar(_) => {
bail!(ProjectorError::UnexpectedResultsType("scalar", "tuple"))
}
QueryResults::Coll(_) => bail!(ProjectorError::UnexpectedResultsType("coll", "tuple")),
QueryResults::Tuple(t) => Ok(t),
QueryResults::Rel(_) => bail!(ProjectorError::UnexpectedResultsType("rel", "tuple")),
}
}
pub fn into_rel(self) -> Result<RelResult<Binding>> {
match self {
QueryResults::Scalar(_) => {
bail!(ProjectorError::UnexpectedResultsType("scalar", "rel"))
}
QueryResults::Coll(_) => bail!(ProjectorError::UnexpectedResultsType("coll", "rel")),
QueryResults::Tuple(_) => bail!(ProjectorError::UnexpectedResultsType("tuple", "rel")),
QueryResults::Rel(r) => Ok(r),
}
}
}
type Index = usize; enum TypedIndex {
Known(Index, ValueTypeTag),
Unknown(Index, Index),
}
impl TypedIndex {
fn lookup(&self, row: &dyn SqlRow) -> Result<Binding> {
use crate::TypedIndex::*;
match *self {
Known(value_index, value_type) => {
let v = row.get_value(value_index)?;
TypedValue::from_sql(v, value_type)
.map(|v| v.into())
.map_err(|e| e.into())
}
Unknown(value_index, type_index) => {
let v = row.get_value(value_index)?;
let value_type_tag = row.get_i64(type_index)? as i32;
TypedValue::from_sql(v, value_type_tag)
.map(|v| v.into())
.map_err(|e| e.into())
}
}
}
}
pub struct CombinedProjection {
pub sql_projection: Projection,
pub pre_aggregate_projection: Option<Projection>,
pub datalog_projector: Box<dyn Projector>,
pub distinct: bool,
pub group_by_cols: Vec<GroupBy>,
}
impl CombinedProjection {
fn flip_distinct_for_limit(mut self, limit: &Limit) -> Self {
if *limit == Limit::Fixed(1) {
self.distinct = false;
}
self
}
}
trait IsPull {
fn is_pull(&self) -> bool;
}
impl IsPull for Element {
fn is_pull(&self) -> bool {
matches!(*self, Element::Pull(_))
}
}
pub fn query_projection(
schema: &Schema,
query: &AlgebraicQuery,
) -> Result<Either<ConstantProjector, CombinedProjection>> {
use self::FindSpec::*;
let spec = query.find_spec.clone();
if query.is_fully_unit_bound() {
let variables: BTreeSet<Variable> = spec
.columns()
.map(|e| match *e {
Element::Variable(ref var) | Element::Corresponding(ref var) => var.clone(),
Element::Pull(_) => {
unreachable!();
}
Element::Aggregate(ref _agg) => {
unreachable!();
}
})
.collect();
let results = QueryOutput::from_constants(&spec, query.cc.value_bindings(&variables));
let f = Box::new(move || results.clone());
Ok(Either::Left(ConstantProjector::new(spec, f)))
} else if query.is_known_empty() {
let empty = QueryOutput::empty_factory(&spec);
Ok(Either::Left(ConstantProjector::new(spec, empty)))
} else {
match *query.find_spec {
FindColl(ref element) => {
let elements = project_elements(1, iter::once(element), query)?;
if element.is_pull() {
CollTwoStagePullProjector::combine(spec, elements)
} else {
CollProjector::combine(spec, elements)
}
.map(|p| p.flip_distinct_for_limit(&query.limit))
}
FindScalar(ref element) => {
let elements = project_elements(1, iter::once(element), query)?;
if element.is_pull() {
ScalarTwoStagePullProjector::combine(schema, spec, elements)
} else {
ScalarProjector::combine(spec, elements)
}
}
FindRel(ref elements) => {
let is_pull = elements.iter().any(|e| e.is_pull());
let column_count = query.find_spec.expected_column_count();
let elements = project_elements(column_count, elements, query)?;
if is_pull {
RelTwoStagePullProjector::combine(spec, column_count, elements)
} else {
RelProjector::combine(spec, column_count, elements)
}
.map(|p| p.flip_distinct_for_limit(&query.limit))
}
FindTuple(ref elements) => {
let is_pull = elements.iter().any(|e| e.is_pull());
let column_count = query.find_spec.expected_column_count();
let elements = project_elements(column_count, elements, query)?;
if is_pull {
TupleTwoStagePullProjector::combine(spec, column_count, elements)
} else {
TupleProjector::combine(spec, column_count, elements)
}
}
}
.map(Either::Right)
}
}
#[test]
fn test_into_tuple() {
let query_output = QueryOutput {
spec: Rc::new(FindSpec::FindTuple(vec![
Element::Variable(Variable::from_valid_name("?x")),
Element::Variable(Variable::from_valid_name("?y")),
])),
results: QueryResults::Tuple(Some(vec![
Binding::Scalar(TypedValue::Long(0)),
Binding::Scalar(TypedValue::Long(2)),
])),
};
assert_eq!(
query_output.clone().into_tuple().expect("into_tuple"),
Some((
Binding::Scalar(TypedValue::Long(0)),
Binding::Scalar(TypedValue::Long(2))
))
);
match query_output.into_tuple() {
Err(ProjectorError::UnexpectedResultsTupleLength(expected, got)) => {
assert_eq!((expected, got), (3, 2));
}
Ok(Some((_, _, _))) => panic!("expected error"),
#[allow(clippy::wildcard_in_or_patterns)]
_ => panic!("expected error"),
}
let query_output = QueryOutput {
spec: Rc::new(FindSpec::FindTuple(vec![
Element::Variable(Variable::from_valid_name("?x")),
Element::Variable(Variable::from_valid_name("?y")),
])),
results: QueryResults::Tuple(None),
};
match query_output.clone().into_tuple() {
Ok(None) => {}
Ok(Some((_, _))) => panic!("expected error"),
#[allow(clippy::wildcard_in_or_patterns)]
_ => panic!("expected error"),
}
match query_output.into_tuple() {
Err(ProjectorError::UnexpectedResultsTupleLength(expected, got)) => {
assert_eq!((expected, got), (3, 2));
}
Ok(Some((_, _, _))) => panic!("expected error"),
#[allow(clippy::wildcard_in_or_patterns)]
_ => panic!("expected error"),
}
}