Compare commits

...

4 commits

Author SHA1 Message Date
Greg Burd
85f0497cf5 WIP 2020-01-24 11:45:54 -05:00
Greg Burd
131398758b Integrate https://github.com/mozilla/mentat/pull/806 2020-01-23 14:31:28 -05:00
Greg Burd
0fa466dd6b Minor fixes. 2020-01-23 14:26:46 -05:00
Greg Burd
b838259a8b rustfmt 2020-01-23 13:16:19 -05:00
44 changed files with 3260 additions and 302 deletions

2447
798.patch Normal file

File diff suppressed because it is too large Load diff

View file

@ -19,11 +19,11 @@ use edn::types::Value;
/// Declare a lazy static `ident` of type `Value::Keyword` with the given `namespace` and
/// `name`.
///
/// It may look surprising that we declare a new `lazy_static!` block rather than including
/// It may look surprising to declare a new `lazy_static!` block rather than including
/// invocations inside an existing `lazy_static!` block. The latter cannot be done, since macros
/// are expanded outside-in. Looking at the `lazy_static!` source suggests that there is no harm in
/// repeating that macro, since internally a multi-`static` block is expanded into many
/// single-`static` blocks.
/// will be expanded outside-in. Looking at the `lazy_static!` source suggests that there is no
/// harm in repeating that macro, since internally a multi-`static` block will be expanded into
/// many single-`static` blocks.
///
/// TODO: take just ":db.part/db" and define DB_PART_DB using "db.part" and "db".
macro_rules! lazy_static_namespaced_keyword_value (

View file

@ -662,7 +662,7 @@ pub struct AttributeCaches {
non_unique_reverse: BTreeMap<Entid, NonUniqueReverseAttributeCache>,
}
// TODO: if an entity or attribute is ever renumbered, the cache will need to be rebuilt.
// TODO: if an entity or attribute is ever re-numbered, the cache will need to be rebuilt.
impl AttributeCaches {
//
// These function names are brief and local.

177
docs/tutorial.md Normal file
View file

@ -0,0 +1,177 @@
# Introduction
Mentat is a transactional, relational storage system built on top of SQLite. The abstractions it offers allow you to easily tackle some things that are tricky in other storage systems:
- Have multiple components share storage and collaborate.
- Evolve schema.
- Track change over time.
- Synchronize data correctly.
- Store data with rich, checked types.
Mentat offers a programmatic Rust API for managing stores, retrieving data, and _transacting_ new data. It offers a Datalog-based query engine, with queries expressed in EDN, a rich textual data format similar to JSON. And it offers an EDN data format for transacting new data.
This tutorial covers all of these APIs, along with defining vocabulary.
We'll begin by introducing some concepts, and then we'll walk through some examples.
## What does Mentat store?
Depending on your perspective, Mentat looks like a relational store, a graph store, or a tuple store.
Mentat stores relationships between _entities_ and other entities or _values_. An entity is related to other things by an _attribute_.
All entities have an _entity ID_ (abbreviated to _entid_).
Some entities additionally have an identifier called an _ident_, which is a keyword. That looks something like `:bookmark/title`.
A value is a primitive piece of data. Mentat supports the following:
* Strings
* Long integers
* Double-precision floating point numbers
* Millisecond-precision timestamps
* UUIDs
* Booleans
* Keywords (a special kind of string that we use for idents).
There are two special kinds of entities: _attributes_ and _transactions_.
Attributes are themselves entities with a particular set of properties that define their meaning. They have identifiers, so you can refer to them easily. They have a _value type_, which is the type of value Mentat expects to be on the right hand side of the relationship. And they have a _cardinality_ (whether one or many values can exist for a particular entity), whether values are _unique_, a documentation string, and some indexing options.
An attribute looks something like this:
```edn
{:db/ident :bookmark/title
:db/cardinality :db.cardinality/one
:db/valueType :db.type/string
:db/fulltext true
:db/doc "The title of a bookmark."}
```
Transactions are special entities that can be described however you wish. By default they track the timestamp at which they were written.
The relationship between an entity, an attribute, and a value, occurring in a _transaction_ (which is just another kind of entity!) — a tuple of five values — is called a _datom_.
A single datom might look something like this:
```
[:db/add 65536 :bookmark/title "mozilla.org" 268435456]
^ ^ ^ ^ ^
\ Add or retract. | | | |
\ The entity. | | |
\ The attribute. | |
\ The value, a string. |
\ The transaction ID.
```
which is equivalent to saying "in transaction 268435456 we assert that entity 65536 is a bookmark with the title 'mozilla.org'".
When we transact that — which means to add it as a fact to the store — Mentat also describes the transaction itself on our behalf:
```edn
[:db/add 268435456 :db/txInstant "2018-01-25 20:07:04.408358 UTC" 268435456]
```
# A simple app
Let's get started with some Rust code.
First, the imports we'll need. The comments here briefly explain what each thing is.
```rust
// So you can define keywords with neater syntax.
#[macro_use(kw)]
extern crate mentat;
use mentat::{
Store, // A single database connection and in-memory metadata.
}
use mentat::vocabulary::attribute; // Properties of attributes.
```
## Defining a simple vocabulary
All data in Mentat — even the terms we used above, like `:db/cardinality` — are defined in the store itself. So that's where we start. In Rust, we define a _vocabulary definition_, and ask the store to ensure that it exists.
```rust
fn set_up(mut store: Store) -> Result<()> {
// Start a write transaction.
let mut in_progress = store.begin_transaction()?;
// Make sure the core vocabulary exists. This is good practice if a user,
// an external tool, or another component might have touched the file
// since you last wrote it.
in_progress.verify_core_schema()?;
// Make sure our vocabulary is installed, and install if necessary.
// This defines some attributes that we can use to describe people.
in_progress.ensure_vocabulary(&Definition {
name: kw!(:example/people),
version: 1,
attributes: vec![
(kw!(:person/name),
vocabulary::AttributeBuilder::default()
.value_type(ValueType::String)
.multival(true)
.build()),
(kw!(:person/age),
vocabulary::AttributeBuilder::default()
.value_type(ValueType::Long)
.multival(false)
.build()),
(kw!(:person/email),
vocabulary::AttributeBuilder::default()
.value_type(ValueType::String)
.multival(true)
.unique(attribute::Unique::Identity)
.build()),
],
})?;
in_progress.commit()?;
Ok(())
}
```
We open a store and configure its vocabulary like this:
```rust
let path = "/path/to/file.db";
let store = Store::open(path)?;
set_up(store)?;
```
If this code returns successfully, we're good to go.
## Transactions
You'll see in our `set_up` function that we begin and end a transaction, which we call `in_progress`. A read-only transaction is begun via `begin_read`. The resulting objects — `InProgress` and `InProgressRead` support various kinds of read and write operations. Transactions are automatically rolled back when dropped, so remember to call `commit`!
## Adding some data
There are two ways to add data to Mentat: programmatically or textually.
The textual form accepts EDN, a simple relative of JSON that supports richer types and more flexible syntax. You saw this in the introduction. Here's an example:
```rust
in_progress.transact(r#"[
{:person/name "Alice"
:person/age 32
:person/email "alice@example.org"}
]"#)?;
```
You can implicitly _upsert_ data when you have a unique attribute to use:
```rust
// Alice's age is now 33. Note that we don't need to find out an entid,
// nor explicitly INSERT OR REPLACE or UPDATE OR INSERT or similar.
in_progress.transact(r#"[
{:person/age 33
:person/email "alice@example.org"}
]"#)?;
```

View file

@ -12,8 +12,8 @@ extern crate chrono;
extern crate itertools;
extern crate num;
extern crate ordered_float;
extern crate pretty;
extern crate peg;
extern crate pretty;
extern crate uuid;
#[cfg(feature = "serde_support")]
@ -50,13 +50,11 @@ pub use types::{
pub use symbols::{Keyword, NamespacedSymbol, PlainSymbol};
use std::collections::{BTreeSet, BTreeMap, LinkedList};
use std::collections::{BTreeMap, BTreeSet, LinkedList};
use std::f64::{INFINITY, NAN, NEG_INFINITY};
use std::iter::FromIterator;
use std::f64::{NAN, INFINITY, NEG_INFINITY};
use chrono::{
TimeZone,
};
use chrono::TimeZone;
use entities::*;
use query::FromValue;
@ -70,7 +68,7 @@ use query::FromValue;
// TODO: Support tagged elements
// TODO: Support discard
pub type ParseError = peg::error::ParseError<peg::str::LineCol>;
pub type ParseErrorKind = peg::error::ParseError<peg::str::LineCol>;
peg::parser!(pub grammar parse() for str {
@ -525,4 +523,3 @@ peg::parser!(pub grammar parse() for str {
/ v:variable() { query::Binding::BindScalar(v) }
});

View file

@ -23,7 +23,11 @@ use num::traits::{One, Zero};
use ordered_float::OrderedFloat;
use chrono::{TimeZone, Utc};
use edn::{parse, symbols, types::{Span, SpannedValue, Value, ValueAndSpan}, utils, ParseError};
use edn::{
parse, symbols,
types::{Span, SpannedValue, Value, ValueAndSpan},
utils, ParseErrorKind,
};
// Helper for making wrapped keywords with a namespace.
fn k_ns(ns: &str, name: &str) -> Value {

View file

@ -16,6 +16,12 @@ use std::collections::BTreeSet;
use std::error::Error;
use rusqlite;
use failure::{
Backtrace,
Context,
Fail,
};
+use std::fmt;
use uuid;
use edn;
@ -39,8 +45,51 @@ use serde_json;
pub type Result<T> = std::result::Result<T, MentatError>;
#[derive(Debug)]
pub struct MentatError(Box<Context<MentatErrorKind>>);
impl Fail for MentatError {
#[inline]
fn cause(&self) -> Option<&Fail> {
self.0.cause()
}
#[inline]
fn backtrace(&self) -> Option<&Backtrace> {
self.0.backtrace()
}
}
impl fmt::Display for MentatError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&*self.0, f)
}
}
impl MentatError {
#[inline]
pub fn kind(&self) -> &MentatErrorKind {
&*self.0.get_context()
}
}
impl From<MentatErrorKind> for MentatError {
#[inline]
fn from(kind: MentatErrorKind) -> MentatError {
MentatError(Box::new(Context::new(kind)))
}
}
impl From<Context<MentatErrorKind>> for MentatError {
#[inline]
fn from(inner: Context<MentatErrorKind>) -> MentatError {
MentatError(Box::new(inner))
}
}
#[derive(Debug, Fail)]
pub enum MentatError {
pub enum MentatErrorKind {
#[fail(display = "bad uuid {}", _0)]
BadUuid(String),
@ -140,9 +189,67 @@ pub enum MentatError {
SerializationError(#[cause] serde_json::Error),
}
impl From<std::io::Error> for MentatErrorKind {
fn from(error: std::io::Error) -> MentatErrorKind {
MentatErrorKind::IoError(error)
}
}
impl From<rusqlite::Error> for MentatErrorKind {
fn from(error: rusqlite::Error) -> MentatErrorKind {
MentatErrorKind::RusqliteError(error.to_string())
}
}
impl From<edn::ParseError> for MentatErrorKind {
fn from(error: edn::ParseError) -> MentatErrorKind {
MentatErrorKind::EdnParseError(error)
}
}
impl From<mentat_db::DbError> for MentatErrorKind {
fn from(error: mentat_db::DbError) -> MentatErrorKind {
MentatErrorKind::DbError(error)
}
}
impl From<mentat_query_algebrizer::AlgebrizerError> for MentatErrorKind {
fn from(error: mentat_query_algebrizer::AlgebrizerError) -> MentatErrorKind {
MentatErrorKind::AlgebrizerError(error)
}
}
impl From<mentat_query_projector::ProjectorError> for MentatErrorKind {
fn from(error: mentat_query_projector::ProjectorError) -> MentatErrorKind {
MentatErrorKind::ProjectorError(error)
}
}
impl From<mentat_query_pull::PullError> for MentatErrorKind {
fn from(error: mentat_query_pull::PullError) -> MentatErrorKind {
MentatErrorKind::PullError(error)
}
}
impl From<mentat_sql::SQLError> for MentatErrorKind {
fn from(error: mentat_sql::SQLError) -> MentatErrorKind {
MentatErrorKind::SQLError(error)
}
}
#[cfg(feature = "syncable")]
impl From<mentat_tolstoy::TolstoyError> for MentatErrorKind {
fn from(error: mentat_tolstoy::TolstoyError) -> MentatErrorKind {
MentatErrorKind::TolstoyError(error)
}
}
// XXX reduce dupe if this isn't completely throwaway
impl From<std::io::Error> for MentatError {
fn from(error: std::io::Error) -> Self {
MentatError::IoError(error)
MentatError::from(error).into()
}
}
@ -152,76 +259,76 @@ impl From<rusqlite::Error> for MentatError {
Some(e) => e.to_string(),
None => "".to_string(),
};
MentatError::RusqliteError(error.to_string(), cause)
MentatError::from(error).into()
}
}
impl From<uuid::Error> for MentatError {
fn from(error: uuid::Error) -> Self {
MentatError::UuidError(error)
MentatError::from(error).into()
}
}
impl From<edn::ParseError> for MentatError {
fn from(error: edn::ParseError) -> Self {
MentatError::EdnParseError(error)
MentatError:from(error).into()
}
}
impl From<DbError> for MentatError {
fn from(error: DbError) -> Self {
MentatError::DbError(error)
MentatError::from(error).into()
}
}
impl From<AlgebrizerError> for MentatError {
fn from(error: AlgebrizerError) -> Self {
MentatError::AlgebrizerError(error)
MentatError::from(error).into()
}
}
impl From<ProjectorError> for MentatError {
fn from(error: ProjectorError) -> Self {
MentatError::ProjectorError(error)
MentatError::from(error).into()
}
}
impl From<PullError> for MentatError {
fn from(error: PullError) -> Self {
MentatError::PullError(error)
MentatError::from(error).into()
}
}
impl From<SQLError> for MentatError {
fn from(error: SQLError) -> Self {
MentatError::SQLError(error)
MentatError::from(error).into()
}
}
#[cfg(feature = "syncable")]
impl From<TolstoyError> for MentatError {
fn from(error: TolstoyError) -> Self {
MentatError::TolstoyError(error)
MentatError::from(error).into()
}
}
#[cfg(feature = "syncable")]
impl From<serde_json::Error> for MentatError {
fn from(error: serde_json::Error) -> Self {
MentatError::SerializationError(error)
MentatError::from(error).into()
}
}
#[cfg(feature = "syncable")]
impl From<hyper::Error> for MentatError {
fn from(error: hyper::Error) -> Self {
MentatError::NetworkError(error)
MentatError::from(error).into()
}
}
#[cfg(feature = "syncable")]
impl From<http::uri::InvalidUri> for MentatError {
fn from(error: http::uri::InvalidUri) -> Self {
MentatError::UriError(error)
MentatError::from(error).into()
}
}

View file

@ -12,10 +12,60 @@ use std; // To refer to std::result::Result.
use core_traits::{ValueType, ValueTypeSet};
use edn::{ ParseError, query::PlainSymbol };
use std::fmt;
use failure::{
Backtrace,
Context,
Fail,
};
use edn::{query::PlainSymbol, ParseErrorKind};
pub type Result<T> = std::result::Result<T, AlgebrizerError>;
#[derive(Debug)]
pub struct AlgebrizerError(Box<Context<AlgebrizerErrorKind>>);
impl Fail for AlgebrizerError {
#[inline]
fn cause(&self) -> Option<&dyn Fail> {
self.0.cause()
}
#[inline]
fn backtrace(&self) -> Option<&Backtrace> {
self.0.backtrace()
}
}
impl fmt::Display for AlgebrizerError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&*self.0, f)
}
}
impl AlgebrizerError {
#[inline]
pub fn kind(&self) -> &AlgebrizerErrorKind {
&*self.0.get_context()
}
}
impl From<AlgebrizerErrorKind> for AlgebrizerError {
#[inline]
fn from(kind: AlgebrizerErrorKind) -> AlgebrizerError {
AlgebrizerError(Box::new(Context::new(kind)))
}
}
impl From<Context<AlgebrizerErrorKind>> for AlgebrizerError {
#[inline]
fn from(inner: Context<AlgebrizerErrorKind>) -> AlgebrizerError {
AlgebrizerError(Box::new(inner))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum BindingError {
NoBoundVariable,
@ -40,7 +90,7 @@ pub enum BindingError {
}
#[derive(Clone, Debug, Eq, Fail, PartialEq)]
pub enum AlgebrizerError {
pub enum AlgebrizerErrorKind {
#[fail(display = "{} var {} is duplicated", _0, _1)]
DuplicateVariableError(PlainSymbol, &'static str),
@ -107,11 +157,11 @@ pub enum AlgebrizerError {
InvalidBinding(PlainSymbol, BindingError),
#[fail(display = "{}", _0)]
EdnParseError(#[cause] ParseError),
EdnParseError(#[cause] ParseErrorKind),
}
impl From<ParseError> for AlgebrizerError {
fn from(error: ParseError) -> AlgebrizerError {
AlgebrizerError::EdnParseError(error)
impl From<ParseErrorKind> for AlgebrizerError {
fn from(error: ParseErrorKind) -> AlgebrizerError {
AlgebrizerError::from(error).into()
}
}

View file

@ -9,9 +9,6 @@
// specific language governing permissions and limitations under the License.
extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate core_traits;
extern crate edn;

View file

@ -16,7 +16,7 @@ use edn::query::{FnArg, NonIntegerConstant, Variable};
use clauses::ConjoiningClauses;
use query_algebrizer_traits::errors::{AlgebrizerError, Result};
use query_algebrizer_traits::errors::{AlgebrizerErrorKind, Result};
use types::EmptyBecause;
@ -62,11 +62,11 @@ impl ValueTypes for FnArg {
&FnArg::Constant(NonIntegerConstant::BigInteger(_)) => {
// Not yet implemented.
bail!(AlgebrizerError::UnsupportedArgument)
bail!(AlgebrizerErrorKind::UnsupportedArgument)
}
// These don't make sense here. TODO: split FnArg into scalar and non-scalar…
&FnArg::Vector(_) | &FnArg::SrcVar(_) => bail!(AlgebrizerError::UnsupportedArgument),
&FnArg::Vector(_) | &FnArg::SrcVar(_) => bail!(AlgebrizerErrorKind::UnsupportedArgument),
// These are all straightforward.
&FnArg::Constant(NonIntegerConstant::Boolean(_)) => {
@ -191,7 +191,7 @@ impl ConjoiningClauses {
FnArg::Variable(in_var) => {
// TODO: technically you could ground an existing variable inside the query….
if !self.input_variables.contains(&in_var) {
bail!(AlgebrizerError::UnboundVariable((*in_var.0).clone()))
bail!(AlgebrizerErrorKind::UnboundVariable((*in_var.0).clone()))
}
match self.bound_value(&in_var) {
// The type is already known if it's a bound variable….
@ -200,7 +200,7 @@ impl ConjoiningClauses {
// The variable is present in `:in`, but it hasn't yet been provided.
// This is a restriction we will eventually relax: we don't yet have a way
// to collect variables as part of a computed table or substitution.
bail!(AlgebrizerError::UnboundVariable((*in_var.0).clone()))
bail!(AlgebrizerErrorKind::UnboundVariable((*in_var.0).clone()))
}
}
}
@ -209,7 +209,7 @@ impl ConjoiningClauses {
FnArg::Constant(NonIntegerConstant::BigInteger(_)) => unimplemented!(),
// These don't make sense here.
FnArg::Vector(_) | FnArg::SrcVar(_) => bail!(AlgebrizerError::InvalidGroundConstant),
FnArg::Vector(_) | FnArg::SrcVar(_) => bail!(AlgebrizerErrorKind::InvalidGroundConstant),
// These are all straightforward.
FnArg::Constant(NonIntegerConstant::Boolean(x)) => {

View file

@ -18,7 +18,7 @@ use edn::query::{Binding, FnArg, NonIntegerConstant, SrcVar, VariableOrPlacehold
use clauses::ConjoiningClauses;
use query_algebrizer_traits::errors::{AlgebrizerError, BindingError, Result};
use query_algebrizer_traits::errors::{AlgebrizerErrorKind, BindingError, Result};
use types::{
Column, ColumnConstraint, DatomsColumn, DatomsTable, EmptyBecause, FulltextColumn,
@ -31,7 +31,7 @@ impl ConjoiningClauses {
#[allow(unused_variables)]
pub(crate) fn apply_fulltext(&mut self, known: Known, where_fn: WhereFn) -> Result<()> {
if where_fn.args.len() != 3 {
bail!(AlgebrizerError::InvalidNumberOfArguments(
bail!(AlgebrizerErrorKind::InvalidNumberOfArguments(
where_fn.operator.clone(),
where_fn.args.len(),
3
@ -40,7 +40,7 @@ impl ConjoiningClauses {
if where_fn.binding.is_empty() {
// The binding must introduce at least one bound variable.
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::NoBoundVariable
));
@ -48,7 +48,7 @@ impl ConjoiningClauses {
if !where_fn.binding.is_valid() {
// The binding must not duplicate bound variables.
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::RepeatedBoundVariable
));
@ -59,7 +59,7 @@ impl ConjoiningClauses {
Binding::BindRel(bindings) => {
let bindings_count = bindings.len();
if bindings_count < 1 || bindings_count > 4 {
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::InvalidNumberOfBindings {
number: bindings.len(),
@ -70,7 +70,7 @@ impl ConjoiningClauses {
bindings
}
Binding::BindScalar(_) | Binding::BindTuple(_) | Binding::BindColl(_) => {
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::ExpectedBindRel
))
@ -93,7 +93,7 @@ impl ConjoiningClauses {
// TODO: process source variables.
match args.next().unwrap() {
FnArg::SrcVar(SrcVar::DefaultSrc) => {}
_ => bail!(AlgebrizerError::InvalidArgument(
_ => bail!(AlgebrizerErrorKind::InvalidArgument(
where_fn.operator.clone(),
"source variable",
0
@ -116,12 +116,12 @@ impl ConjoiningClauses {
// TODO: allow non-constant attributes.
match self.bound_value(&v) {
Some(TypedValue::Ref(entid)) => Some(entid),
Some(tv) => bail!(AlgebrizerError::InputTypeDisagreement(
Some(tv) => bail!(AlgebrizerErrorKind::InputTypeDisagreement(
v.name().clone(),
ValueType::Ref,
tv.value_type()
)),
None => bail!(AlgebrizerError::UnboundVariable((*v.0).clone())),
None => bail!(AlgebrizerErrorKind::UnboundVariable((*v.0).clone())),
}
}
_ => None,
@ -130,7 +130,7 @@ impl ConjoiningClauses {
// An unknown ident, or an entity that isn't present in the store, or isn't a fulltext
// attribute, is likely enough to be a coding error that we choose to bail instead of
// marking the pattern as known-empty.
let a = a.ok_or(AlgebrizerError::InvalidArgument(
let a = a.ok_or(AlgebrizerErrorKind::InvalidArgument(
where_fn.operator.clone(),
"attribute",
1,
@ -139,7 +139,7 @@ impl ConjoiningClauses {
schema
.attribute_for_entid(a)
.cloned()
.ok_or(AlgebrizerError::InvalidArgument(
.ok_or(AlgebrizerErrorKind::InvalidArgument(
where_fn.operator.clone(),
"attribute",
1,
@ -190,7 +190,7 @@ impl ConjoiningClauses {
FnArg::Variable(in_var) => {
match self.bound_value(&in_var) {
Some(t @ TypedValue::String(_)) => Either::Left(t),
Some(_) => bail!(AlgebrizerError::InvalidArgument(
Some(_) => bail!(AlgebrizerErrorKind::InvalidArgument(
where_fn.operator.clone(),
"string",
2
@ -199,7 +199,7 @@ impl ConjoiningClauses {
// Regardless of whether we'll be providing a string later, or the value
// comes from a column, it must be a string.
if self.known_type(&in_var) != Some(ValueType::String) {
bail!(AlgebrizerError::InvalidArgument(
bail!(AlgebrizerErrorKind::InvalidArgument(
where_fn.operator.clone(),
"string",
2
@ -209,7 +209,7 @@ impl ConjoiningClauses {
if self.input_variables.contains(&in_var) {
// Sorry, we haven't implemented late binding.
// TODO: implement this.
bail!(AlgebrizerError::UnboundVariable((*in_var.0).clone()))
bail!(AlgebrizerErrorKind::UnboundVariable((*in_var.0).clone()))
} else {
// It must be bound earlier in the query. We already established that
// it must be a string column.
@ -220,13 +220,13 @@ impl ConjoiningClauses {
{
Either::Right(binding)
} else {
bail!(AlgebrizerError::UnboundVariable((*in_var.0).clone()))
bail!(AlgebrizerErrorKind::UnboundVariable((*in_var.0).clone()))
}
}
}
}
}
_ => bail!(AlgebrizerError::InvalidArgument(
_ => bail!(AlgebrizerErrorKind::InvalidArgument(
where_fn.operator.clone(),
"string",
2
@ -298,7 +298,7 @@ impl ConjoiningClauses {
// We do not allow the score to be bound.
if self.value_bindings.contains_key(var) || self.input_variables.contains(var) {
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
var.name(),
BindingError::UnexpectedBinding
));

View file

@ -18,7 +18,7 @@ use clauses::{ConjoiningClauses, PushComputed};
use clauses::convert::ValueConversion;
use query_algebrizer_traits::errors::{AlgebrizerError, BindingError, Result};
use query_algebrizer_traits::errors::{AlgebrizerErrorKind, BindingError, Result};
use types::{ComputedTable, EmptyBecause, SourceAlias, VariableColumn};
@ -117,7 +117,7 @@ impl ConjoiningClauses {
pub(crate) fn apply_ground(&mut self, known: Known, where_fn: WhereFn) -> Result<()> {
if where_fn.args.len() != 1 {
bail!(AlgebrizerError::InvalidNumberOfArguments(
bail!(AlgebrizerErrorKind::InvalidNumberOfArguments(
where_fn.operator.clone(),
where_fn.args.len(),
1
@ -128,7 +128,7 @@ impl ConjoiningClauses {
if where_fn.binding.is_empty() {
// The binding must introduce at least one bound variable.
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::NoBoundVariable
));
@ -136,7 +136,7 @@ impl ConjoiningClauses {
if !where_fn.binding.is_valid() {
// The binding must not duplicate bound variables.
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::RepeatedBoundVariable
));
@ -154,7 +154,7 @@ impl ConjoiningClauses {
// Just the same, but we bind more than one column at a time.
if children.len() != places.len() {
// Number of arguments don't match the number of values. TODO: better error message.
bail!(AlgebrizerError::GroundBindingsMismatch)
bail!(AlgebrizerErrorKind::GroundBindingsMismatch)
}
for (place, arg) in places.into_iter().zip(children.into_iter()) {
self.apply_ground_place(schema, place, arg)? // TODO: short-circuit on impossible.
@ -168,7 +168,7 @@ impl ConjoiningClauses {
// are all in a single structure. That makes it substantially simpler!
(Binding::BindColl(var), FnArg::Vector(children)) => {
if children.is_empty() {
bail!(AlgebrizerError::InvalidGroundConstant)
bail!(AlgebrizerErrorKind::InvalidGroundConstant)
}
// Turn a collection of arguments into a Vec of `TypedValue`s of the same type.
@ -188,7 +188,7 @@ impl ConjoiningClauses {
&& !accumulated_types.is_unit()
{
// Values not all of the same type.
Some(Err(AlgebrizerError::InvalidGroundConstant.into()))
Some(Err(AlgebrizerErrorKind::InvalidGroundConstant.into()))
} else {
Some(Ok(tv))
}
@ -219,7 +219,7 @@ impl ConjoiningClauses {
(Binding::BindRel(places), FnArg::Vector(rows)) => {
if rows.is_empty() {
bail!(AlgebrizerError::InvalidGroundConstant)
bail!(AlgebrizerErrorKind::InvalidGroundConstant)
}
// Grab the known types to which these args must conform, and track
@ -243,7 +243,7 @@ impl ConjoiningClauses {
if expected_width == 0 {
// They can't all be placeholders.
bail!(AlgebrizerError::InvalidGroundConstant)
bail!(AlgebrizerErrorKind::InvalidGroundConstant)
}
// Accumulate values into `matrix` and types into `a_t_f_c`.
@ -259,7 +259,7 @@ impl ConjoiningClauses {
FnArg::Vector(cols) => {
// Make sure that every row is the same length.
if cols.len() != full_width {
bail!(AlgebrizerError::InvalidGroundConstant)
bail!(AlgebrizerErrorKind::InvalidGroundConstant)
}
// TODO: don't accumulate twice.
@ -297,12 +297,12 @@ impl ConjoiningClauses {
let inserted = acc.insert(val.value_type());
if inserted && !acc.is_unit() {
// Heterogeneous types.
bail!(AlgebrizerError::InvalidGroundConstant)
bail!(AlgebrizerErrorKind::InvalidGroundConstant)
}
matrix.push(val);
}
}
_ => bail!(AlgebrizerError::InvalidGroundConstant),
_ => bail!(AlgebrizerErrorKind::InvalidGroundConstant),
}
}
@ -329,7 +329,7 @@ impl ConjoiningClauses {
self.collect_named_bindings(schema, names, types, matrix);
Ok(())
}
(_, _) => bail!(AlgebrizerError::InvalidGroundConstant),
(_, _) => bail!(AlgebrizerErrorKind::InvalidGroundConstant),
}
}
}

View file

@ -14,7 +14,7 @@ use core_traits::{TypedValue, ValueType};
use edn::query::Variable;
use query_algebrizer_traits::errors::{AlgebrizerError, Result};
use query_algebrizer_traits::errors::{AlgebrizerErrorKind, Result};
/// Define the inputs to a query. This is in two parts: a set of values known now, and a set of
/// types known now.
@ -69,7 +69,7 @@ impl QueryInputs {
let old = types.insert(var.clone(), t);
if let Some(old) = old {
if old != t {
bail!(AlgebrizerError::InputTypeDisagreement(var.name(), old, t));
bail!(AlgebrizerErrorKind::InputTypeDisagreement(var.name(), old, t));
}
}
}

View file

@ -24,7 +24,7 @@ use mentat_core::counter::RcCounter;
use edn::query::{Element, FindSpec, Keyword, PatternNonValuePlace, Pull, Variable, WhereClause};
use query_algebrizer_traits::errors::{AlgebrizerError, Result};
use query_algebrizer_traits::errors::{AlgebrizerErrorKind, Result};
use types::{
Column, ColumnConstraint, ColumnIntersection, ComputedTable, DatomsColumn, DatomsTable,
@ -1071,7 +1071,7 @@ impl ConjoiningClauses {
let qa = self
.extracted_types
.get(&var)
.ok_or_else(|| AlgebrizerError::UnboundVariable(var.name()))?;
.ok_or_else(|| AlgebrizerErrorKind::UnboundVariable(var.name()))?;
self.wheres.add_intersection(ColumnConstraint::HasTypes {
value: qa.0.clone(),
value_types: types,

View file

@ -12,7 +12,7 @@ use edn::query::{ContainsVariables, NotJoin, UnifyVars};
use clauses::ConjoiningClauses;
use query_algebrizer_traits::errors::{AlgebrizerError, Result};
use query_algebrizer_traits::errors::{AlgebrizerErrorKind, Result};
use types::{ColumnConstraint, ComputedTable};
@ -35,7 +35,7 @@ impl ConjoiningClauses {
let col = self.column_bindings.get(&v).unwrap()[0].clone();
template.column_bindings.insert(v.clone(), vec![col]);
} else {
bail!(AlgebrizerError::UnboundVariable(v.name()));
bail!(AlgebrizerErrorKind::UnboundVariable(v.name()));
}
}
@ -89,7 +89,7 @@ mod testing {
use clauses::{add_attribute, associate_ident, QueryInputs};
use query_algebrizer_traits::errors::AlgebrizerError;
use query_algebrizer_traits::errors::AlgebrizerErrorKind;
use types::{
ColumnAlternation, ColumnConstraint, ColumnConstraintOrAlternation, ColumnIntersection,
@ -714,7 +714,7 @@ mod testing {
let parsed = parse_find_string(query).expect("parse failed");
let err = algebrize(known, parsed).expect_err("algebrization should have failed");
match err {
AlgebrizerError::UnboundVariable(var) => {
AlgebrizerErrorKind::UnboundVariable(var) => {
assert_eq!(var, PlainSymbol("?x".to_string()));
}
x => panic!("expected Unbound Variable error, got {:?}", x),

View file

@ -18,7 +18,7 @@ use clauses::ConjoiningClauses;
use clauses::convert::ValueTypes;
use query_algebrizer_traits::errors::{AlgebrizerError, Result};
use query_algebrizer_traits::errors::{AlgebrizerErrorKind, Result};
use types::{ColumnConstraint, EmptyBecause, Inequality, QueryValue};
@ -38,7 +38,7 @@ impl ConjoiningClauses {
if let Some(op) = Inequality::from_datalog_operator(predicate.operator.0.as_str()) {
self.apply_inequality(known, op, predicate)
} else {
bail!(AlgebrizerError::UnknownFunction(predicate.operator.clone()))
bail!(AlgebrizerErrorKind::UnknownFunction(predicate.operator.clone()))
}
}
@ -56,7 +56,7 @@ impl ConjoiningClauses {
Some(value_type) => {
self.add_type_requirement(anno.variable.clone(), ValueTypeSet::of_one(value_type))
}
None => bail!(AlgebrizerError::InvalidArgumentType(
None => bail!(AlgebrizerErrorKind::InvalidArgumentType(
PlainSymbol::plain("type"),
ValueTypeSet::any(),
2
@ -76,7 +76,7 @@ impl ConjoiningClauses {
predicate: Predicate,
) -> Result<()> {
if predicate.args.len() != 2 {
bail!(AlgebrizerError::InvalidNumberOfArguments(
bail!(AlgebrizerErrorKind::InvalidNumberOfArguments(
predicate.operator.clone(),
predicate.args.len(),
2
@ -97,7 +97,7 @@ impl ConjoiningClauses {
.potential_types(known.schema, &left)?
.intersection(&supported_types);
if left_types.is_empty() {
bail!(AlgebrizerError::InvalidArgumentType(
bail!(AlgebrizerErrorKind::InvalidArgumentType(
predicate.operator.clone(),
supported_types,
0
@ -108,7 +108,7 @@ impl ConjoiningClauses {
.potential_types(known.schema, &right)?
.intersection(&supported_types);
if right_types.is_empty() {
bail!(AlgebrizerError::InvalidArgumentType(
bail!(AlgebrizerErrorKind::InvalidArgumentType(
predicate.operator.clone(),
supported_types,
1
@ -160,7 +160,7 @@ impl ConjoiningClauses {
left_v = self.resolve_ref_argument(known.schema, &predicate.operator, 0, left)?;
right_v = self.resolve_ref_argument(known.schema, &predicate.operator, 1, right)?;
} else {
bail!(AlgebrizerError::InvalidArgumentType(
bail!(AlgebrizerErrorKind::InvalidArgumentType(
predicate.operator.clone(),
supported_types,
0

View file

@ -16,7 +16,7 @@ use edn::query::{FnArg, NonIntegerConstant, PlainSymbol};
use clauses::ConjoiningClauses;
use query_algebrizer_traits::errors::{AlgebrizerError, Result};
use query_algebrizer_traits::errors::{AlgebrizerErrorKind, Result};
use types::{EmptyBecause, QueryValue};
@ -41,14 +41,14 @@ impl ConjoiningClauses {
if v.value_type().is_numeric() {
Ok(QueryValue::TypedValue(v))
} else {
bail!(AlgebrizerError::InputTypeDisagreement(var.name().clone(), ValueType::Long, v.value_type()))
bail!(AlgebrizerErrorKind::InputTypeDisagreement(var.name().clone(), ValueType::Long, v.value_type()))
}
} else {
self.constrain_var_to_numeric(var.clone());
self.column_bindings
.get(&var)
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
.ok_or_else(|| AlgebrizerError::UnboundVariable(var.name()).into())
.ok_or_else(|| AlgebrizerErrorKind::UnboundVariable(var.name()).into())
}
},
// Can't be an entid.
@ -62,7 +62,7 @@ impl ConjoiningClauses {
Constant(NonIntegerConstant::BigInteger(_)) |
Vector(_) => {
self.mark_known_empty(EmptyBecause::NonNumericArgument);
bail!(AlgebrizerError::InvalidArgument(function.clone(), "numeric", position))
bail!(AlgebrizerErrorKind::InvalidArgument(function.clone(), "numeric", position))
},
Constant(NonIntegerConstant::Float(f)) => Ok(QueryValue::TypedValue(TypedValue::Double(f))),
}
@ -79,7 +79,7 @@ impl ConjoiningClauses {
match arg {
FnArg::Variable(var) => match self.bound_value(&var) {
Some(TypedValue::Instant(v)) => Ok(QueryValue::TypedValue(TypedValue::Instant(v))),
Some(v) => bail!(AlgebrizerError::InputTypeDisagreement(
Some(v) => bail!(AlgebrizerErrorKind::InputTypeDisagreement(
var.name().clone(),
ValueType::Instant,
v.value_type()
@ -89,7 +89,7 @@ impl ConjoiningClauses {
self.column_bindings
.get(&var)
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
.ok_or_else(|| AlgebrizerError::UnboundVariable(var.name()).into())
.ok_or_else(|| AlgebrizerErrorKind::UnboundVariable(var.name()).into())
}
},
Constant(NonIntegerConstant::Instant(v)) => {
@ -107,7 +107,7 @@ impl ConjoiningClauses {
| Constant(NonIntegerConstant::BigInteger(_))
| Vector(_) => {
self.mark_known_empty(EmptyBecause::NonInstantArgument);
bail!(AlgebrizerError::InvalidArgumentType(
bail!(AlgebrizerErrorKind::InvalidArgumentType(
function.clone(),
ValueType::Instant.into(),
position
@ -136,14 +136,14 @@ impl ConjoiningClauses {
self.column_bindings
.get(&var)
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
.ok_or_else(|| AlgebrizerError::UnboundVariable(var.name()).into())
.ok_or_else(|| AlgebrizerErrorKind::UnboundVariable(var.name()).into())
}
}
EntidOrInteger(i) => Ok(QueryValue::TypedValue(TypedValue::Ref(i))),
IdentOrKeyword(i) => schema
.get_entid(&i)
.map(|known_entid| QueryValue::Entid(known_entid.into()))
.ok_or_else(|| AlgebrizerError::UnrecognizedIdent(i.to_string()).into()),
.ok_or_else(|| AlgebrizerErrorKind::UnrecognizedIdent(i.to_string()).into()),
Constant(NonIntegerConstant::Boolean(_))
| Constant(NonIntegerConstant::Float(_))
| Constant(NonIntegerConstant::Text(_))
@ -153,7 +153,7 @@ impl ConjoiningClauses {
| SrcVar(_)
| Vector(_) => {
self.mark_known_empty(EmptyBecause::NonEntityArgument);
bail!(AlgebrizerError::InvalidArgumentType(
bail!(AlgebrizerErrorKind::InvalidArgumentType(
function.clone(),
ValueType::Ref.into(),
position
@ -188,7 +188,7 @@ impl ConjoiningClauses {
.column_bindings
.get(&var)
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
.ok_or_else(|| AlgebrizerError::UnboundVariable(var.name()).into()),
.ok_or_else(|| AlgebrizerErrorKind::UnboundVariable(var.name()).into()),
},
EntidOrInteger(i) => Ok(QueryValue::PrimitiveLong(i)),
IdentOrKeyword(_) => unimplemented!(), // TODO

View file

@ -14,7 +14,7 @@ use edn::query::{Binding, FnArg, SrcVar, VariableOrPlaceholder, WhereFn};
use clauses::ConjoiningClauses;
use query_algebrizer_traits::errors::{AlgebrizerError, BindingError, Result};
use query_algebrizer_traits::errors::{AlgebrizerErrorKind, BindingError, Result};
use types::{
Column, ColumnConstraint, DatomsTable, Inequality, QualifiedAlias, QueryValue, SourceAlias,
@ -40,7 +40,7 @@ impl ConjoiningClauses {
// transactions that impact one of the given attributes.
pub(crate) fn apply_tx_ids(&mut self, known: Known, where_fn: WhereFn) -> Result<()> {
if where_fn.args.len() != 3 {
bail!(AlgebrizerError::InvalidNumberOfArguments(
bail!(AlgebrizerErrorKind::InvalidNumberOfArguments(
where_fn.operator.clone(),
where_fn.args.len(),
3
@ -49,7 +49,7 @@ impl ConjoiningClauses {
if where_fn.binding.is_empty() {
// The binding must introduce at least one bound variable.
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::NoBoundVariable
));
@ -57,7 +57,7 @@ impl ConjoiningClauses {
if !where_fn.binding.is_valid() {
// The binding must not duplicate bound variables.
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::RepeatedBoundVariable
));
@ -68,7 +68,7 @@ impl ConjoiningClauses {
Binding::BindRel(bindings) => {
let bindings_count = bindings.len();
if bindings_count != 1 {
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::InvalidNumberOfBindings {
number: bindings_count,
@ -83,7 +83,7 @@ impl ConjoiningClauses {
}
Binding::BindColl(v) => v,
Binding::BindScalar(_) | Binding::BindTuple(_) => {
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::ExpectedBindRelOrBindColl
))
@ -95,7 +95,7 @@ impl ConjoiningClauses {
// TODO: process source variables.
match args.next().unwrap() {
FnArg::SrcVar(SrcVar::DefaultSrc) => {}
_ => bail!(AlgebrizerError::InvalidArgument(
_ => bail!(AlgebrizerErrorKind::InvalidArgument(
where_fn.operator.clone(),
"source variable",
0
@ -150,7 +150,7 @@ impl ConjoiningClauses {
pub(crate) fn apply_tx_data(&mut self, known: Known, where_fn: WhereFn) -> Result<()> {
if where_fn.args.len() != 2 {
bail!(AlgebrizerError::InvalidNumberOfArguments(
bail!(AlgebrizerErrorKind::InvalidNumberOfArguments(
where_fn.operator.clone(),
where_fn.args.len(),
2
@ -159,7 +159,7 @@ impl ConjoiningClauses {
if where_fn.binding.is_empty() {
// The binding must introduce at least one bound variable.
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::NoBoundVariable
));
@ -167,7 +167,7 @@ impl ConjoiningClauses {
if !where_fn.binding.is_valid() {
// The binding must not duplicate bound variables.
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::RepeatedBoundVariable
));
@ -178,7 +178,7 @@ impl ConjoiningClauses {
Binding::BindRel(bindings) => {
let bindings_count = bindings.len();
if bindings_count < 1 || bindings_count > 5 {
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::InvalidNumberOfBindings {
number: bindings.len(),
@ -189,7 +189,7 @@ impl ConjoiningClauses {
bindings
}
Binding::BindScalar(_) | Binding::BindTuple(_) | Binding::BindColl(_) => {
bail!(AlgebrizerError::InvalidBinding(
bail!(AlgebrizerErrorKind::InvalidBinding(
where_fn.operator.clone(),
BindingError::ExpectedBindRel
))
@ -217,7 +217,7 @@ impl ConjoiningClauses {
// TODO: process source variables.
match args.next().unwrap() {
FnArg::SrcVar(SrcVar::DefaultSrc) => {}
_ => bail!(AlgebrizerError::InvalidArgument(
_ => bail!(AlgebrizerErrorKind::InvalidArgument(
where_fn.operator.clone(),
"source variable",
0

View file

@ -12,7 +12,7 @@ use edn::query::WhereFn;
use clauses::ConjoiningClauses;
use query_algebrizer_traits::errors::{AlgebrizerError, Result};
use query_algebrizer_traits::errors::{AlgebrizerErrorKind, Result};
use Known;
@ -32,7 +32,7 @@ impl ConjoiningClauses {
"ground" => self.apply_ground(known, where_fn),
"tx-data" => self.apply_tx_data(known, where_fn),
"tx-ids" => self.apply_tx_ids(known, where_fn),
_ => bail!(AlgebrizerError::UnknownFunction(where_fn.operator.clone())),
_ => bail!(AlgebrizerErrorKind::UnknownFunction(where_fn.operator.clone())),
}
}
}

View file

@ -32,7 +32,7 @@ use mentat_core::counter::RcCounter;
use edn::query::{Element, FindSpec, Limit, Order, ParsedQuery, SrcVar, Variable, WhereClause};
use query_algebrizer_traits::errors::{AlgebrizerError, Result};
use query_algebrizer_traits::errors::{AlgebrizerErrorKind, Result};
pub use clauses::{QueryInputs, VariableBindings};
@ -229,7 +229,7 @@ fn validate_and_simplify_order(
// Fail if the var isn't bound by the query.
if !cc.column_bindings.contains_key(&var) {
bail!(AlgebrizerError::UnboundVariable(var.name()))
bail!(AlgebrizerErrorKind::UnboundVariable(var.name()))
}
// Otherwise, determine if we also need to order by type…
@ -263,7 +263,7 @@ fn simplify_limit(mut query: AlgebraicQuery) -> Result<AlgebraicQuery> {
Some(TypedValue::Long(n)) => {
if n <= 0 {
// User-specified limits should always be natural numbers (> 0).
bail!(AlgebrizerError::InvalidLimit(
bail!(AlgebrizerErrorKind::InvalidLimit(
n.to_string(),
ValueType::Long
))
@ -273,7 +273,7 @@ fn simplify_limit(mut query: AlgebraicQuery) -> Result<AlgebraicQuery> {
}
Some(val) => {
// Same.
bail!(AlgebrizerError::InvalidLimit(
bail!(AlgebrizerErrorKind::InvalidLimit(
format!("{:?}", val),
val.value_type()
))
@ -375,7 +375,7 @@ impl FindQuery {
for var in parsed.in_vars.into_iter() {
if !set.insert(var.clone()) {
bail!(AlgebrizerError::DuplicateVariableError(var.name(), ":in"));
bail!(AlgebrizerErrorKind::DuplicateVariableError(var.name(), ":in"));
}
}
@ -387,7 +387,7 @@ impl FindQuery {
for var in parsed.with.into_iter() {
if !set.insert(var.clone()) {
bail!(AlgebrizerError::DuplicateVariableError(var.name(), ":with"));
bail!(AlgebrizerErrorKind::DuplicateVariableError(var.name(), ":with"));
}
}
@ -397,7 +397,7 @@ impl FindQuery {
// Make sure that if we have `:limit ?x`, `?x` appears in `:in`.
if let Limit::Variable(ref v) = parsed.limit {
if !in_vars.contains(v) {
bail!(AlgebrizerError::UnknownLimitVar(v.name()));
bail!(AlgebrizerErrorKind::UnknownLimitVar(v.name()));
}
}

View file

@ -12,7 +12,7 @@ use std::collections::BTreeSet;
use edn::query::{ContainsVariables, NotJoin, OrJoin, UnifyVars, Variable};
use query_algebrizer_traits::errors::{AlgebrizerError, Result};
use query_algebrizer_traits::errors::{AlgebrizerErrorKind, Result};
/// In an `or` expression, every mentioned var is considered 'free'.
/// In an `or-join` expression, every var in the var list is 'required'.
@ -47,7 +47,7 @@ pub(crate) fn validate_or_join(or_join: &OrJoin) -> Result<()> {
let template = clauses.next().unwrap().collect_mentioned_variables();
for clause in clauses {
if template != clause.collect_mentioned_variables() {
bail!(AlgebrizerError::NonMatchingVariablesInOrClause)
bail!(AlgebrizerErrorKind::NonMatchingVariablesInOrClause)
}
}
Ok(())
@ -58,7 +58,7 @@ pub(crate) fn validate_or_join(or_join: &OrJoin) -> Result<()> {
let var_set: BTreeSet<Variable> = vars.iter().cloned().collect();
for clause in &or_join.clauses {
if !var_set.is_subset(&clause.collect_mentioned_variables()) {
bail!(AlgebrizerError::NonMatchingVariablesInOrClause)
bail!(AlgebrizerErrorKind::NonMatchingVariablesInOrClause)
}
}
Ok(())
@ -74,7 +74,7 @@ pub(crate) fn validate_not_join(not_join: &NotJoin) -> Result<()> {
// The joined vars must each appear somewhere in the clause's mentioned variables.
let var_set: BTreeSet<Variable> = vars.iter().cloned().collect();
if !var_set.is_subset(&not_join.collect_mentioned_variables()) {
bail!(AlgebrizerError::NonMatchingVariablesInNotClause)
bail!(AlgebrizerErrorKind::NonMatchingVariablesInNotClause)
}
Ok(())
}

View file

@ -24,7 +24,7 @@ use mentat_core::Schema;
use edn::query::{Keyword, PlainSymbol, Variable};
use query_algebrizer_traits::errors::{AlgebrizerError, BindingError};
use query_algebrizer_traits::errors::{AlgebrizerErrorKind, BindingError};
use mentat_query_algebrizer::{ComputedTable, Known, QueryInputs};
@ -297,7 +297,7 @@ fn test_ground_coll_heterogeneous_types() {
let q = r#"[:find ?x :where [?x _ ?v] [(ground [false 8.5]) [?v ...]]]"#;
let schema = prepopulated_schema();
let known = Known::for_schema(&schema);
assert_eq!(bails(known, &q), AlgebrizerError::InvalidGroundConstant);
assert_eq!(bails(known, &q), AlgebrizerErrorKind::InvalidGroundConstant);
}
#[test]
@ -305,7 +305,7 @@ fn test_ground_rel_heterogeneous_types() {
let q = r#"[:find ?x :where [?x _ ?v] [(ground [[false] [5]]) [[?v]]]]"#;
let schema = prepopulated_schema();
let known = Known::for_schema(&schema);
assert_eq!(bails(known, &q), AlgebrizerError::InvalidGroundConstant);
assert_eq!(bails(known, &q), AlgebrizerErrorKind::InvalidGroundConstant);
}
#[test]
@ -315,7 +315,7 @@ fn test_ground_tuple_duplicate_vars() {
let known = Known::for_schema(&schema);
assert_eq!(
bails(known, &q),
AlgebrizerError::InvalidBinding(
AlgebrizerErrorKind::InvalidBinding(
PlainSymbol::plain("ground"),
BindingError::RepeatedBoundVariable
)
@ -329,7 +329,7 @@ fn test_ground_rel_duplicate_vars() {
let known = Known::for_schema(&schema);
assert_eq!(
bails(known, &q),
AlgebrizerError::InvalidBinding(
AlgebrizerErrorKind::InvalidBinding(
PlainSymbol::plain("ground"),
BindingError::RepeatedBoundVariable
)
@ -343,7 +343,7 @@ fn test_ground_nonexistent_variable_invalid() {
let known = Known::for_schema(&schema);
assert_eq!(
bails(known, &q),
AlgebrizerError::UnboundVariable(PlainSymbol::plain("?v"))
AlgebrizerErrorKind::UnboundVariable(PlainSymbol::plain("?v"))
);
}
@ -362,6 +362,6 @@ fn test_unbound_input_variable_invalid() {
assert_eq!(
bails_with_inputs(known, &q, i),
AlgebrizerError::UnboundVariable(PlainSymbol::plain("?x"))
AlgebrizerErrorKind::UnboundVariable(PlainSymbol::plain("?x"))
);
}

View file

@ -22,7 +22,7 @@ use mentat_core::{DateTime, Schema, Utc};
use edn::query::{Keyword, PlainSymbol, Variable};
use query_algebrizer_traits::errors::AlgebrizerError;
use query_algebrizer_traits::errors::AlgebrizerErrorKind;
use mentat_query_algebrizer::{EmptyBecause, Known, QueryInputs};
@ -75,7 +75,7 @@ fn test_instant_predicates_require_instants() {
[(> ?t "2017-06-16T00:56:41.257Z")]]"#;
assert_eq!(
bails(known, query),
AlgebrizerError::InvalidArgumentType(
AlgebrizerErrorKind::InvalidArgumentType(
PlainSymbol::plain(">"),
ValueTypeSet::of_numeric_and_instant_types(),
1
@ -88,7 +88,7 @@ fn test_instant_predicates_require_instants() {
[(> "2017-06-16T00:56:41.257Z", ?t)]]"#;
assert_eq!(
bails(known, query),
AlgebrizerError::InvalidArgumentType(
AlgebrizerErrorKind::InvalidArgumentType(
PlainSymbol::plain(">"),
ValueTypeSet::of_numeric_and_instant_types(),
0

View file

@ -16,7 +16,7 @@ use mentat_query_algebrizer::{ColumnName, ConjoiningClauses, VariableColumn};
use mentat_query_sql::{ColumnOrExpression, Expression, Name, ProjectedColumn};
use errors::{ProjectorError, Result};
use errors::{ProjectorErrorKind, Result};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SimpleAggregationOp {
@ -60,7 +60,7 @@ impl SimpleAggregationOp {
pub fn is_applicable_to_types(&self, possibilities: ValueTypeSet) -> Result<ValueType> {
use self::SimpleAggregationOp::*;
if possibilities.is_empty() {
bail!(ProjectorError::CannotProjectImpossibleBinding(*self))
bail!(ProjectorErrorKind::CannotProjectImpossibleBinding(*self))
}
match self {
@ -73,7 +73,7 @@ impl SimpleAggregationOp {
// The mean of a set of numeric values will always, for our purposes, be a double.
Ok(ValueType::Double)
} else {
bail!(ProjectorError::CannotApplyAggregateOperationToTypes(
bail!(ProjectorErrorKind::CannotApplyAggregateOperationToTypes(
*self,
possibilities
))
@ -88,7 +88,7 @@ impl SimpleAggregationOp {
Ok(ValueType::Long)
}
} else {
bail!(ProjectorError::CannotApplyAggregateOperationToTypes(
bail!(ProjectorErrorKind::CannotApplyAggregateOperationToTypes(
*self,
possibilities
))
@ -111,7 +111,7 @@ impl SimpleAggregationOp {
// These types are unordered.
Keyword | Ref | Uuid => {
bail!(ProjectorError::CannotApplyAggregateOperationToTypes(
bail!(ProjectorErrorKind::CannotApplyAggregateOperationToTypes(
*self,
possibilities
))
@ -129,7 +129,7 @@ impl SimpleAggregationOp {
Ok(ValueType::Long)
}
} else {
bail!(ProjectorError::CannotApplyAggregateOperationToTypes(
bail!(ProjectorErrorKind::CannotApplyAggregateOperationToTypes(
*self,
possibilities
))

View file

@ -15,14 +15,60 @@ use rusqlite;
use core_traits::ValueTypeSet;
use db_traits::errors::DbError;
use edn::query::PlainSymbol;
use failure::{ Backtrace, Context, Fail, };
use std::fmt;
use query_pull_traits::errors::PullError;
use aggregates::SimpleAggregationOp;
pub type Result<T> = std::result::Result<T, ProjectorError>;
#[derive(Debug)]
pub struct ProjectorError(Box<Context<ProjectorErrorKind>>);
impl Fail for ProjectorError {
#[inline]
fn cause(&self) -> Option<&Fail> {
self.0.cause()
}
#[inline]
fn backtrace(&self) -> Option<&Backtrace> {
self.0.backtrace()
}
}
impl fmt::Display for ProjectorError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&*self.0, f)
}
}
impl ProjectorError {
#[inline]
pub fn kind(&self) -> &ProjectorErrorKind {
&*self.0.get_context()
}
}
impl From<ProjectorErrorKind> for ProjectorError {
#[inline]
fn from(kind: ProjectorErrorKind) -> ProjectorError {
ProjectorError(Box::new(Context::new(kind)))
}
}
impl From<Context<ProjectorErrorKind>> for ProjectorError {
#[inline]
fn from(inner: Context<ProjectorErrorKind>) -> ProjectorError {
ProjectorError(Box::new(inner))
}
}
#[derive(Debug, Fail)]
pub enum ProjectorError {
pub enum ProjectorErrorKind {
/// We're just not done yet. Message that the feature is recognized but not yet
/// implemented.
#[fail(display = "not yet implemented: {}", _0)]
@ -70,6 +116,24 @@ pub enum ProjectorError {
PullError(#[cause] PullError),
}
impl From<rusqlite::Error> for ProjectorErrorKind {
fn from(error: rusqlite::Error) -> ProjectorErrorKind {
ProjectorErrorKind::from(error).into()
}
}
impl From<mentat_db::DbError> for ProjectorErrorKind {
fn from(error: mentat_db::DbError) -> ProjectorErrorKind {
ProjectorErrorKind::from(error).into()
}
}
impl From<mentat_query_pull::PullError> for ProjectorErrorKind {
fn from(error: mentat_query_pull::PullError) -> ProjectorErrorKind {
ProjectorErrorKind::from(error).into()
}
}
impl From<rusqlite::Error> for ProjectorError {
fn from(error: rusqlite::Error) -> ProjectorError {
ProjectorError::RusqliteError(error.to_string())

View file

@ -101,9 +101,9 @@ fn test_the_without_max_or_min() {
// … when we look at the projection list, we cannot reconcile the types.
let projection = query_projection(&schema, &algebrized);
assert!(projection.is_err());
use query_projector_traits::errors::ProjectorError;
use query_projector_traits::errors::ProjectorErrorKind;
match projection.err().expect("expected failure") {
ProjectorError::InvalidProjection(s) => {
ProjectorErrorKind::InvalidProjection(s) => {
assert_eq!(s.as_str(), "Warning: used `the` without `min` or `max`.");
}
_ => panic!(),

View file

@ -10,7 +10,7 @@
use core_traits::Binding;
use query_projector_traits::errors::{ProjectorError, Result};
use query_projector_traits::errors::{ProjectorErrorKind, Result};
/// A `BindingTuple` is any type that can accommodate a Mentat tuple query result of fixed length.
///
@ -27,7 +27,7 @@ impl BindingTuple for Vec<Binding> {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(
Err(ProjectorErrorKind::UnexpectedResultsTupleLength(
expected,
vec.len(),
))
@ -43,13 +43,13 @@ impl BindingTuple for Vec<Binding> {
impl BindingTuple for (Binding,) {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
if expected != 1 {
return Err(ProjectorError::UnexpectedResultsTupleLength(1, expected));
return Err(ProjectorErrorKind::UnexpectedResultsTupleLength(1, expected));
}
match vec {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(
Err(ProjectorErrorKind::UnexpectedResultsTupleLength(
expected,
vec.len(),
))
@ -65,13 +65,13 @@ impl BindingTuple for (Binding,) {
impl BindingTuple for (Binding, Binding) {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
if expected != 2 {
return Err(ProjectorError::UnexpectedResultsTupleLength(2, expected));
return Err(ProjectorErrorKind::UnexpectedResultsTupleLength(2, expected));
}
match vec {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(
Err(ProjectorErrorKind::UnexpectedResultsTupleLength(
expected,
vec.len(),
))
@ -87,13 +87,13 @@ impl BindingTuple for (Binding, Binding) {
impl BindingTuple for (Binding, Binding, Binding) {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
if expected != 3 {
return Err(ProjectorError::UnexpectedResultsTupleLength(3, expected));
return Err(ProjectorErrorKind::UnexpectedResultsTupleLength(3, expected));
}
match vec {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(
Err(ProjectorErrorKind::UnexpectedResultsTupleLength(
expected,
vec.len(),
))
@ -113,13 +113,13 @@ impl BindingTuple for (Binding, Binding, Binding) {
impl BindingTuple for (Binding, Binding, Binding, Binding) {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
if expected != 4 {
return Err(ProjectorError::UnexpectedResultsTupleLength(4, expected));
return Err(ProjectorErrorKind::UnexpectedResultsTupleLength(4, expected));
}
match vec {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(
Err(ProjectorErrorKind::UnexpectedResultsTupleLength(
expected,
vec.len(),
))
@ -140,13 +140,13 @@ impl BindingTuple for (Binding, Binding, Binding, Binding) {
impl BindingTuple for (Binding, Binding, Binding, Binding, Binding) {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
if expected != 5 {
return Err(ProjectorError::UnexpectedResultsTupleLength(5, expected));
return Err(ProjectorErrorKind::UnexpectedResultsTupleLength(5, expected));
}
match vec {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(
Err(ProjectorErrorKind::UnexpectedResultsTupleLength(
expected,
vec.len(),
))
@ -170,13 +170,13 @@ impl BindingTuple for (Binding, Binding, Binding, Binding, Binding) {
impl BindingTuple for (Binding, Binding, Binding, Binding, Binding, Binding) {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
if expected != 6 {
return Err(ProjectorError::UnexpectedResultsTupleLength(6, expected));
return Err(ProjectorErrorKind::UnexpectedResultsTupleLength(6, expected));
}
match vec {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(
Err(ProjectorErrorKind::UnexpectedResultsTupleLength(
expected,
vec.len(),
))

View file

@ -69,7 +69,7 @@ use projectors::{
pub use relresult::{RelResult, StructuredRelResult};
use query_projector_traits::errors::{ProjectorError, Result};
use query_projector_traits::errors::{ProjectorErrorKind, Result};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QueryOutput {
@ -275,43 +275,43 @@ impl QueryResults {
pub fn into_scalar(self) -> Result<Option<Binding>> {
match self {
QueryResults::Scalar(o) => Ok(o),
QueryResults::Coll(_) => bail!(ProjectorError::UnexpectedResultsType("coll", "scalar")),
QueryResults::Coll(_) => bail!(ProjectorErrorKind::UnexpectedResultsType("coll", "scalar")),
QueryResults::Tuple(_) => {
bail!(ProjectorError::UnexpectedResultsType("tuple", "scalar"))
bail!(ProjectorErrorKind::UnexpectedResultsType("tuple", "scalar"))
}
QueryResults::Rel(_) => bail!(ProjectorError::UnexpectedResultsType("rel", "scalar")),
QueryResults::Rel(_) => bail!(ProjectorErrorKind::UnexpectedResultsType("rel", "scalar")),
}
}
pub fn into_coll(self) -> Result<Vec<Binding>> {
match self {
QueryResults::Scalar(_) => {
bail!(ProjectorError::UnexpectedResultsType("scalar", "coll"))
bail!(ProjectorErrorKind::UnexpectedResultsType("scalar", "coll"))
}
QueryResults::Coll(c) => Ok(c),
QueryResults::Tuple(_) => bail!(ProjectorError::UnexpectedResultsType("tuple", "coll")),
QueryResults::Rel(_) => bail!(ProjectorError::UnexpectedResultsType("rel", "coll")),
QueryResults::Tuple(_) => bail!(ProjectorErrorKind::UnexpectedResultsType("tuple", "coll")),
QueryResults::Rel(_) => bail!(ProjectorErrorKind::UnexpectedResultsType("rel", "coll")),
}
}
pub fn into_tuple(self) -> Result<Option<Vec<Binding>>> {
match self {
QueryResults::Scalar(_) => {
bail!(ProjectorError::UnexpectedResultsType("scalar", "tuple"))
bail!(ProjectorErrorKind::UnexpectedResultsType("scalar", "tuple"))
}
QueryResults::Coll(_) => bail!(ProjectorError::UnexpectedResultsType("coll", "tuple")),
QueryResults::Coll(_) => bail!(ProjectorErrorKind::UnexpectedResultsType("coll", "tuple")),
QueryResults::Tuple(t) => Ok(t),
QueryResults::Rel(_) => bail!(ProjectorError::UnexpectedResultsType("rel", "tuple")),
QueryResults::Rel(_) => bail!(ProjectorErrorKind::UnexpectedResultsType("rel", "tuple")),
}
}
pub fn into_rel(self) -> Result<RelResult<Binding>> {
match self {
QueryResults::Scalar(_) => {
bail!(ProjectorError::UnexpectedResultsType("scalar", "rel"))
bail!(ProjectorErrorKind::UnexpectedResultsType("scalar", "rel"))
}
QueryResults::Coll(_) => bail!(ProjectorError::UnexpectedResultsType("coll", "rel")),
QueryResults::Tuple(_) => bail!(ProjectorError::UnexpectedResultsType("tuple", "rel")),
QueryResults::Coll(_) => bail!(ProjectorErrorKind::UnexpectedResultsType("coll", "rel")),
QueryResults::Tuple(_) => bail!(ProjectorErrorKind::UnexpectedResultsType("tuple", "rel")),
QueryResults::Rel(r) => Ok(r),
}
}
@ -526,7 +526,7 @@ fn test_into_tuple() {
);
match query_output.clone().into_tuple() {
Err(ProjectorError::UnexpectedResultsTupleLength(expected, got)) => {
Err(ProjectorErrorKind::UnexpectedResultsTupleLength(expected, got)) => {
assert_eq!((expected, got), (3, 2));
}
// This forces the result type.
@ -548,7 +548,7 @@ fn test_into_tuple() {
}
match query_output.clone().into_tuple() {
Err(ProjectorError::UnexpectedResultsTupleLength(expected, got)) => {
Err(ProjectorErrorKind::UnexpectedResultsTupleLength(expected, got)) => {
assert_eq!((expected, got), (3, 2));
}
// This forces the result type.

View file

@ -30,7 +30,7 @@ use query_projector_traits::aggregates::{
projected_column_for_simple_aggregate, SimpleAggregation,
};
use query_projector_traits::errors::{ProjectorError, Result};
use query_projector_traits::errors::{ProjectorErrorKind, Result};
use projectors::Projector;
@ -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(|| ProjectorErrorKind::UnboundVariable(var.name()).into())
}
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(|| ProjectorErrorKind::UnboundVariable(var.name()).into())
}
fn candidate_column(cc: &ConjoiningClauses, var: &Variable) -> Result<(ColumnOrExpression, Name)> {
@ -187,13 +187,13 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
match e {
&Element::Variable(ref var) => {
if outer_variables.contains(var) {
bail!(ProjectorError::InvalidProjection(format!(
bail!(ProjectorErrorKind::InvalidProjection(format!(
"Duplicate variable {} in query.",
var
)));
}
if corresponded_variables.contains(var) {
bail!(ProjectorError::InvalidProjection(format!(
bail!(ProjectorErrorKind::InvalidProjection(format!(
"Can't project both {} and `(the {})` from a query.",
var, var
)));
@ -201,13 +201,13 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
}
&Element::Corresponding(ref var) => {
if outer_variables.contains(var) {
bail!(ProjectorError::InvalidProjection(format!(
bail!(ProjectorErrorKind::InvalidProjection(format!(
"Can't project both {} and `(the {})` from a query.",
var, var
)));
}
if corresponded_variables.contains(var) {
bail!(ProjectorError::InvalidProjection(format!(
bail!(ProjectorErrorKind::InvalidProjection(format!(
"`(the {})` appears twice in query.",
var
)));
@ -344,7 +344,7 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
i += 1;
} else {
// TODO: complex aggregates.
bail!(ProjectorError::NotYetImplemented(
bail!(ProjectorErrorKind::NotYetImplemented(
"complex aggregates".into()
));
}
@ -355,7 +355,7 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
match (min_max_count, corresponded_variables.len()) {
(0, 0) | (_, 0) => {}
(0, _) => {
bail!(ProjectorError::InvalidProjection(
bail!(ProjectorErrorKind::InvalidProjection(
"Warning: used `the` without `min` or `max`.".to_string()
));
}
@ -363,7 +363,7 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
// This is the success case!
}
(n, c) => {
bail!(ProjectorError::AmbiguousAggregates(n, c));
bail!(ProjectorErrorKind::AmbiguousAggregates(n, c));
}
}
@ -466,7 +466,7 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
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())
ProjectorErrorKind::NoTypeAvailableForVariable(var.name().clone())
})?;
inner_projection.push(ProjectedColumn(
ColumnOrExpression::Column(type_col),

View file

@ -12,10 +12,50 @@ use std; // To refer to std::result::Result.
use db_traits::errors::DbError;
use failure::{ Backtrace, Context, Fail, };
use core_traits::Entid;
use std::fmt;
pub type Result<T> = std::result::Result<T, PullError>;
#[derive(Debug)]
pub struct PullErrorKind(Box<Context<PullErrorKind>>);
impl Fail for PullError {
#[inline]
fn cause(&self) -> Option<&dyn Fail> {
self.0.cause()
}
#[inline]
fn backtrace(&self) -> Option<&Backtrace> {
self.0.backtrace()
}
}
impl fmt::Display for PullError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&*self.0, f)
}
}
impl PullError {
#[inline]
pub fn kind(&self) -> &PullErrorKind {
&*self.0.get_context()
}
}
impl From<PullErrorKind> for PullError {
#[inline]
fn from(kind: PullErrorKind) -> PullError {
PullErrorKind(Box::new(Context::new(kind)))
}
}
#[derive(Debug, Fail)]
pub enum PullError {
#[fail(display = "attribute {:?} has no name", _0)]
@ -28,8 +68,13 @@ pub enum PullError {
DbError(#[cause] DbError),
}
impl From<DbError> for PullError {
fn from(error: DbError) -> PullError {
PullError::DbError(error)
impl From<DbError> for PullErrorKind {
fn from(error: DbError) -> PullErrorKind {
PullErrorKind::DbError(error)
}
}
impl From<DbError> for PullError {
fn from(error: DbError) -> PullError {
PullErrorKind::from(error).into()
}
}

View file

@ -9,8 +9,6 @@
// specific language governing permissions and limitations under the License.
extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate core_traits;
extern crate db_traits;

View file

@ -77,7 +77,7 @@ use mentat_db::cache;
use edn::query::{NamedPullAttribute, PullAttributeSpec, PullConcreteAttribute};
use query_pull_traits::errors::{PullError, Result};
use query_pull_traits::errors::{PullErrorKind, Result};
type PullResults = BTreeMap<Entid, ValueRc<StructuredMap>>;
@ -149,7 +149,7 @@ impl Puller {
schema
.get_ident(*i)
.map(|ident| ValueRc::new(ident.clone()))
.ok_or_else(|| PullError::UnnamedAttribute(*i))
.ok_or_else(|| PullErrorKind::UnnamedAttribute(*i))
};
let mut names: BTreeMap<Entid, ValueRc<Keyword>> = Default::default();
@ -177,7 +177,7 @@ impl Puller {
&PullConcreteAttribute::Ident(ref i) if i.as_ref() == db_id.as_ref() => {
// We only allow :db/id once.
if db_id_alias.is_some() {
Err(PullError::RepeatedDbId)?
Err(PullErrorKind::RepeatedDbId)?
}
db_id_alias = Some(alias.unwrap_or_else(|| db_id.to_value_rc()));
}

View file

@ -8,8 +8,54 @@
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
use failure::{ Backtrace, Context, Fail, };
use std::fmt;
#[derive(Debug)]
pub struct SQLError(Box<Context<SQLErrorKind>>);
impl Fail for SQLError {
#[inline]
fn cause(&self) -> Option<&dyn Fail> {
self.0.cause()
}
#[inline]
fn backtrace(&self) -> Option<&Backtrace> {
self.0.backtrace()
}
}
impl fmt::Display for SQLError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&*self.0, f)
}
}
impl SQLError {
#[inline]
pub fn kind(&self) -> &SQLErrorKind {
&*self.0.get_context()
}
}
impl From<SQLErrorKind> for SQLError {
#[inline]
fn from(kind: SQLErrorKind) -> SQLError {
SQLError(Box::new(Context::new(kind)))
}
}
impl From<Context<SQLErrorKind>> for SQLError {
#[inline]
fn from(inner: Context<SQLErrorKind>) -> SQLError {
SQLError(Box::new(inner))
}
}
#[derive(Debug, Fail)]
pub enum SQLError {
pub enum SQLErrorKind {
#[fail(display = "invalid parameter name: {}", _0)]
InvalidParameterName(String),

View file

@ -9,7 +9,5 @@
// specific language governing permissions and limitations under the License.
extern crate failure;
#[macro_use]
extern crate failure_derive;
pub mod errors;

View file

@ -18,14 +18,13 @@ extern crate mentat_core;
extern crate sql_traits;
use std::rc::Rc;
use std::collections::HashMap;
use ordered_float::OrderedFloat;
use core_traits::TypedValue;
use sql_traits::errors::{BuildQueryResult, SQLError};
use sql_traits::errors::{BuildQueryResult, SQLErrorKind};
use mentat_core::{ToMicros, ValueRc};
@ -194,7 +193,7 @@ impl QueryBuilder for SQLiteQueryBuilder {
// Do some validation first.
// This is not free, but it's probably worth it for now.
if !name.chars().all(|c| char::is_alphanumeric(c) || c == '_') {
return Err(SQLError::InvalidParameterName(name.to_string()));
return Err(SQLErrorKind::InvalidParameterName(name.to_string()));
}
if name.starts_with(self.arg_prefix.as_str())
@ -203,7 +202,7 @@ impl QueryBuilder for SQLiteQueryBuilder {
.skip(self.arg_prefix.len())
.all(char::is_numeric)
{
return Err(SQLError::BindParamCouldBeGenerated(name.to_string()));
return Err(SQLErrorKind::BindParamCouldBeGenerated(name.to_string()));
}
self.push_sql("$");

View file

@ -36,7 +36,7 @@ use mentat_query_pull::{pull_attributes_for_entities, pull_attributes_for_entity
use mentat_transaction::{CacheAction, CacheDirection, InProgress, InProgressRead, Metadata};
use public_traits::errors::{MentatError, Result};
use public_traits::errors::{MentatErrorKind, Result};
use mentat_transaction::query::{
lookup_value_for_attribute, lookup_values_for_attribute, q_explain, q_once, q_prepare,
@ -340,7 +340,7 @@ impl Conn {
attribute_entid = metadata
.schema
.attribute_for_ident(&attribute)
.ok_or_else(|| MentatError::UnknownAttribute(attribute.to_string()))?
.ok_or_else(|| MentatErrorKind::UnknownAttribute(attribute.to_string()))?
.1
.into();
}
@ -404,12 +404,16 @@ mod tests {
let t = format!("[[:db/add {} :db.schema/attribute \"tempid\"]]", next + 1);
match conn.transact(&mut sqlite, t.as_str()) {
Err(MentatError::DbError(e)) => {
assert_eq!(
e.kind(),
::db_traits::errors::DbErrorKind::UnallocatedEntid(next + 1)
);
}
Err(e) => {
match e.kind() {
&MentatErrorKind::DbError(ref e) => {
assert_eq!(e.kind(), ::mentat_db::DbErrorKind::UnallocatedEntid(next + 1));
}
x => {
panic!("expected db error, got {:?}", x);
}
}
},
x => panic!("expected db error, got {:?}", x),
}
@ -434,12 +438,15 @@ mod tests {
let t = format!("[[:db/add {} :db.schema/attribute \"tempid\"]]", next);
match conn.transact(&mut sqlite, t.as_str()) {
Err(MentatError::DbError(e)) => {
// All this, despite this being the ID we were about to allocate!
assert_eq!(
e.kind(),
::db_traits::errors::DbErrorKind::UnallocatedEntid(next)
);
Err(e) => {
match e.kind() {
&MentatErrorKind::DbError(ref e) => {
assert_eq!(e.kind(), ::mentat_db::DbErrorKind::UnallocatedEntid(next + 1));
}
x => {
panic!("expected db error, got {:?}", x);
}
}
}
x => panic!("expected db error, got {:?}", x),
}
@ -642,8 +649,8 @@ mod tests {
// Bad EDN: missing closing ']'.
let report = conn.transact(&mut sqlite, "[[:db/add \"t\" :db/ident :a/keyword]");
match report.expect_err("expected transact to fail for bad edn") {
MentatError::EdnParseError(_) => {}
match report.expect_err("expected transact to fail for bad edn").kind() {
&MentatErrorKind::EdnParseError(_) => { }
x => panic!("expected EDN parse error, got {:?}", x),
}
@ -655,8 +662,8 @@ mod tests {
// Bad transaction data: missing leading :db/add.
let report = conn.transact(&mut sqlite, "[[\"t\" :db/ident :b/keyword]]");
match report.expect_err("expected transact error") {
MentatError::EdnParseError(_) => {}
match report.expect_err("expected transact error").kind() {
&MentatErrorKind::EdnParseError(_) => { }
x => panic!("expected EDN parse error, got {:?}", x),
}
@ -668,12 +675,10 @@ mod tests {
// Bad transaction based on state of store: conflicting upsert.
let report = conn.transact(
&mut sqlite,
"[[:db/add \"u\" :db/ident :a/keyword]
[:db/add \"u\" :db/ident :b/keyword]]",
);
match report.expect_err("expected transact error") {
MentatError::DbError(e) => match e.kind() {
&mut sqlite, "[[:db/add \"u\" :db/ident :a/keyword]
[:db/add \"u\" :db/ident :b/keyword]]");
match report.expect_err("expected transact error").kind() {
&MentatErrorKind::DbError(ref e) => {
::db_traits::errors::DbErrorKind::SchemaConstraintViolation(_) => {}
_ => panic!("expected SchemaConstraintViolation"),
},
@ -705,8 +710,8 @@ mod tests {
CacheDirection::Forward,
CacheAction::Register,
);
match res.expect_err("expected cache to fail") {
MentatError::UnknownAttribute(msg) => assert_eq!(msg, ":foo/bat"),
match res.expect_err("expected cache to fail").kind() {
&MentatErrorKind::UnknownAttribute(ref msg) => assert_eq!(msg, ":foo/bat")
x => panic!("expected UnknownAttribute error, got {:?}", x),
}
}

View file

@ -82,7 +82,7 @@ macro_rules! kw {
}
pub use public_traits::errors;
pub use public_traits::errors::{MentatError, Result};
pub use public_traits::errors::{MentatErrorKind, Result};
pub use edn::{FromMicros, FromMillis, ParseError, ToMicros, ToMillis};
pub use mentat_query_projector::BindingTuple;

View file

@ -17,7 +17,7 @@ use mentat_core::{DateTime, Keyword, Utc};
use super::{HasSchema, QueryInputs, QueryOutput, Queryable, RelResult, Store, Variable};
use public_traits::errors::{MentatError, Result};
use public_traits::errors::{MentatErrorKind, Result};
pub struct QueryBuilder<'a> {
query: String,
@ -49,12 +49,8 @@ impl<'a> QueryBuilder<'a> {
}
pub fn bind_ref_from_kw(&mut self, var: &str, value: Keyword) -> Result<&mut Self> {
let entid = self
.store
.conn()
.current_schema()
.get_entid(&value)
.ok_or(MentatError::UnknownAttribute(value.to_string()))?;
let entid = self.store.conn().current_schema().get_entid(&value).ok_or_else(||
MentatError::from(MentatErrorKind::UnknownAttribute(value.to_string())))?;
self.values.insert(
Variable::from_valid_name(var),
TypedValue::Ref(entid.into()),

29
src/store.rs.rej Normal file
View file

@ -0,0 +1,29 @@
--- src/store.rs
+++ src/store.rs
@@ -85,7 +85,7 @@ impl Store {
pub fn open_empty(path: &str) -> Result<Store> {
if !path.is_empty() {
if Path::new(path).exists() {
- bail!(MentatError::PathAlreadyExists(path.to_string()));
+ bail!(MentatErrorKind::PathAlreadyExists(path.to_string()));
}
}
@@ -125,7 +125,7 @@ impl Store {
pub fn open_empty_with_key(path: &str, encryption_key: &str) -> Result<Store> {
if !path.is_empty() {
if Path::new(path).exists() {
- bail!(MentatError::PathAlreadyExists(path.to_string()));
+ bail!(MentatErrorKind::PathAlreadyExists(path.to_string()));
}
}
@@ -241,7 +241,7 @@ impl Pullable for Store {
#[cfg(feature = "syncable")]
impl Syncable for Store {
fn sync(&mut self, server_uri: &String, user_uuid: &String) -> Result<()> {
- let uuid = Uuid::parse_str(&user_uuid).map_err(|_| MentatError::BadUuid(user_uuid.clone()))?;
+ let uuid = Uuid::parse_str(&user_uuid).map_err(|_| MentatErrorKind::BadUuid(user_uuid.clone()))?;
Ok(Syncer::flow(&mut self.sqlite, server_uri, &uuid)?)
}
}

View file

@ -101,7 +101,7 @@ use super::{
CORE_SCHEMA_VERSION,
};
use super::errors::{MentatError, Result};
use super::errors::{MentatErrorKind, Result};
use mentat_transaction::{InProgress, Queryable};
@ -325,17 +325,17 @@ where
{
fn core_type(&self, t: ValueType) -> Result<KnownEntid> {
self.entid_for_type(t)
.ok_or_else(|| MentatError::MissingCoreVocabulary(DB_SCHEMA_VERSION.clone()).into())
.ok_or_else(|| MentatErrorKind::MissingCoreVocabulary(DB_SCHEMA_VERSION.clone()).into())
}
fn core_entid(&self, ident: &Keyword) -> Result<KnownEntid> {
self.get_entid(ident)
.ok_or_else(|| MentatError::MissingCoreVocabulary(DB_SCHEMA_VERSION.clone()).into())
.ok_or_else(|| MentatErrorKind::MissingCoreVocabulary(DB_SCHEMA_VERSION.clone()).into())
}
fn core_attribute(&self, ident: &Keyword) -> Result<KnownEntid> {
self.attribute_for_ident(ident)
.ok_or_else(|| MentatError::MissingCoreVocabulary(DB_SCHEMA_VERSION.clone()).into())
.ok_or_else(|| MentatErrorKind::MissingCoreVocabulary(DB_SCHEMA_VERSION.clone()).into())
.map(|(_, e)| e)
}
}
@ -548,7 +548,7 @@ pub trait VersionedStore: HasVocabularies + HasSchema {
// We have two vocabularies with the same name, same version, and
// different definitions for an attribute. That's a coding error.
// We can't accept this vocabulary.
bail!(MentatError::ConflictingAttributeDefinitions(
bail!(MentatErrorKind::ConflictingAttributeDefinitions(
definition.name.to_string(),
definition.version,
pair.0.to_string(),
@ -604,7 +604,7 @@ pub trait VersionedStore: HasVocabularies + HasSchema {
fn verify_core_schema(&self) -> Result<()> {
if let Some(core) = self.read_vocabulary_named(&DB_SCHEMA_CORE)? {
if core.version != CORE_SCHEMA_VERSION {
bail!(MentatError::UnexpectedCoreSchema(
bail!(MentatErrorKind::UnexpectedCoreSchema(
CORE_SCHEMA_VERSION,
Some(core.version)
));
@ -613,7 +613,7 @@ pub trait VersionedStore: HasVocabularies + HasSchema {
// TODO: check things other than the version.
} else {
// This would be seriously messed up.
bail!(MentatError::UnexpectedCoreSchema(CORE_SCHEMA_VERSION, None));
bail!(MentatErrorKind::UnexpectedCoreSchema(CORE_SCHEMA_VERSION, None));
}
Ok(())
}
@ -694,7 +694,7 @@ impl<'a, 'c> VersionedStore for InProgress<'a, 'c> {
self.install_attributes_for(definition, attributes)
}
VocabularyCheck::PresentButTooNew { newer_version } => {
Err(MentatError::ExistingVocabularyTooNew(
Err(MentatErrorKind::ExistingVocabularyTooNew(
definition.name.to_string(),
newer_version.version,
definition.version,
@ -722,7 +722,7 @@ impl<'a, 'c> VersionedStore for InProgress<'a, 'c> {
out.insert(definition.name.clone(), VocabularyOutcome::Existed);
}
VocabularyCheck::PresentButTooNew { newer_version } => {
bail!(MentatError::ExistingVocabularyTooNew(
bail!(MentatErrorKind::ExistingVocabularyTooNew(
definition.name.to_string(),
newer_version.version,
definition.version
@ -914,7 +914,7 @@ where
attributes: attributes,
}))
}
Some(_) => bail!(MentatError::InvalidVocabularyVersion),
Some(_) => bail!(MentatErrorKind::InvalidVocabularyVersion),
}
} else {
Ok(None)

View file

@ -37,7 +37,7 @@ use mentat::query::q_uncached;
use mentat::conn::Conn;
use public_traits::errors::MentatError;
use public_traits::errors::MentatErrorKind;
#[test]
fn test_rel() {
@ -243,10 +243,9 @@ fn test_unbound_inputs() {
inputs,
);
match results.expect_err("expected unbound variables") {
MentatError::UnboundVariables(vars) => {
assert_eq!(vars, vec!["?e".to_string()].into_iter().collect());
}
match results.expect_err("expected unbound variables").kind() {
&MentatErrorKind::UnboundVariables(ref vars) => {
assert_eq!(vars, &vec!["?e".to_string()].into_iter().collect());
_ => panic!("Expected UnboundVariables variant."),
}
}
@ -495,17 +494,15 @@ fn test_fulltext() {
[?a :foo/term ?term]
]"#;
let r = conn.q_once(&mut c, query, None);
match r.expect_err("expected query to fail") {
MentatError::AlgebrizerError(
query_algebrizer_traits::errors::AlgebrizerError::InvalidArgument(
PlainSymbol(s),
ty,
i,
),
) => {
assert_eq!(s, "fulltext");
assert_eq!(ty, "string");
assert_eq!(i, 2);
match r.expect_err("expected query to fail").kind() {
&MentatErrorKind::AlgebrizerError(ref e) => {
if let &mentat_query_algebrizer::AlgebrizerErrorKind::InvalidArgument(PlainSymbol(ref s), ref ty, ref i) = e.kind() {
assert_eq!(*s, "fulltext");
assert_eq!(*ty, "string");
assert_eq!(*i, 2);
} else {
panic!("Expected invalid argument");
}
}
_ => panic!("Expected query to fail."),
}
@ -516,17 +513,15 @@ fn test_fulltext() {
[?a :foo/term ?term]
[(fulltext $ :foo/fts ?a) [[?x ?val]]]]"#;
let r = conn.q_once(&mut c, query, None);
match r.expect_err("expected query to fail") {
MentatError::AlgebrizerError(
query_algebrizer_traits::errors::AlgebrizerError::InvalidArgument(
PlainSymbol(s),
ty,
i,
),
) => {
assert_eq!(s, "fulltext");
assert_eq!(ty, "string");
assert_eq!(i, 2);
match r.expect_err("expected query to fail").kind() {
&MentatErrorKind::AlgebrizerError(ref e) => {
if let &mentat_query_algebrizer::AlgebrizerErrorKind::InvalidArgument(PlainSymbol(ref s), ref ty, ref i) = e.kind() {
assert_eq!(*s, "fulltext");
assert_eq!(*ty, "string");
assert_eq!(*i, 2);
} else {
panic!("expected AlgebrizerError::InvalidArgument");
}
}
_ => panic!("Expected query to fail."),
}
@ -732,14 +727,15 @@ fn test_aggregates_type_handling() {
// No type limits => can't do it.
let r = store.q_once(r#"[:find (sum ?v) . :where [_ _ ?v]]"#, None);
let all_types = ValueTypeSet::any();
match r.expect_err("expected query to fail") {
MentatError::ProjectorError(
::query_projector_traits::errors::ProjectorError::CannotApplyAggregateOperationToTypes(
SimpleAggregationOp::Sum,
types,
),
) => {
assert_eq!(types, all_types);
use mentat_query_projector::errors::ProjectorErrorKind;
match r.expect_err("expected query to fail").kind() {
&MentatErrorKind::ProjectorError(ref e) => {
if let &ProjectorErrorKind::CannotApplyAggregateOperationToTypes(
SimpleAggregationOp::Sum, ref types) = e.kind() {
assert_eq!(types, &all_types);
} else {
panic!("Unexpected error type {:?}", e);
}
}
e => panic!("Unexpected error type {:?}", e),
}
@ -750,14 +746,14 @@ fn test_aggregates_type_handling() {
:where [_ _ ?v] [(type ?v :db.type/instant)]]"#,
None,
);
match r.expect_err("expected query to fail") {
MentatError::ProjectorError(
::query_projector_traits::errors::ProjectorError::CannotApplyAggregateOperationToTypes(
SimpleAggregationOp::Sum,
types,
),
) => {
assert_eq!(types, ValueTypeSet::of_one(ValueType::Instant));
match r.expect_err("expected query to fail").kind() {
&MentatErrorKind::ProjectorError(ref e) => {
if let &ProjectorErrorKind::CannotApplyAggregateOperationToTypes(
SimpleAggregationOp::Sum, ref types) = e.kind() {
assert_eq!(types, &ValueTypeSet::of_one(ValueType::Instant));
} else {
panic!("Unexpected error type {:?}", e);
}
}
e => panic!("Unexpected error type {:?}", e),
}
@ -1705,12 +1701,15 @@ fn test_aggregation_implicit_grouping() {
[?person :foo/name ?name]]"#,
None,
);
match res.expect_err("expected query to fail") {
MentatError::ProjectorError(
::query_projector_traits::errors::ProjectorError::AmbiguousAggregates(mmc, cc),
) => {
assert_eq!(mmc, 2);
assert_eq!(cc, 1);
use mentat_query_projector::errors::ProjectorErrorKind;
match res.expect_err("expected query to fail").kind() {
&MentatErrorKind::ProjectorError(ref e) => {
if let &ProjectorErrorKind::AmbiguousAggregates(mmc, cc) = e.kind() {
assert_eq!(mmc, 2);
assert_eq!(cc, 1);
} else {
panic!("Unexpected error type {:?}.", e);
}
}
e => {
panic!("Unexpected error type {:?}.", e);

View file

@ -40,7 +40,7 @@ use mentat::{
use mentat::entity_builder::{BuildTerms, TermBuilder};
use mentat::errors::MentatError;
use mentat::errors::MentatErrorKind;
lazy_static! {
static ref FOO_NAME: Keyword = { kw!(:foo/name) };
@ -327,12 +327,12 @@ fn test_add_vocab() {
.ensure_vocabulary(&foo_v1_malformed)
.expect_err("expected vocabulary to fail")
{
MentatError::ConflictingAttributeDefinitions(vocab, version, attr, theirs, ours) => {
&MentatErrorKind::ConflictingAttributeDefinitions(vocab, version, attr, theirs, ours) => {
assert_eq!(vocab.as_str(), ":org.mozilla/foo");
assert_eq!(attr.as_str(), ":foo/baz");
assert_eq!(version, 1);
assert_eq!(&theirs, &baz);
assert_eq!(&ours, &malformed_baz);
assert_eq!(*version, 1);
assert_eq!(theirs, &baz);
assert_eq!(ours, &malformed_baz);
}
_ => panic!(),
}

View file

@ -376,7 +376,7 @@ impl Repl {
let next = match encryption_key {
#[cfg(not(feature = "sqlcipher"))]
Some(_) => {
return Err(::mentat::MentatError::RusqliteError(
return Err(::mentat::MentatErrorKind::RusqliteError(
".open_encrypted requires the sqlcipher Mentat feature".into(),
"".into(),
))

View file

@ -43,7 +43,7 @@ pub use mentat_query_projector::{
RelResult,
};
use public_traits::errors::{MentatError, Result};
use public_traits::errors::{MentatErrorKind, Result};
pub type QueryExecutionResult = Result<QueryOutput>;
pub type PreparedResult<'sqlite> = Result<PreparedQuery<'sqlite>>;
@ -156,7 +156,7 @@ where
// If they aren't, the user has made an error -- perhaps writing the wrong variable in `:in`, or
// not binding in the `QueryInput`.
if !unbound.is_empty() {
bail!(MentatError::UnboundVariables(
bail!(MentatErrorKind::UnboundVariables(
unbound.into_iter().map(|v| v.to_string()).collect()
));
}
@ -198,7 +198,7 @@ fn fetch_values<'sqlite>(
fn lookup_attribute(schema: &Schema, attribute: &Keyword) -> Result<KnownEntid> {
schema
.get_entid(attribute)
.ok_or_else(|| MentatError::UnknownAttribute(attribute.name().into()).into())
.ok_or_else(|| MentatErrorKind::UnknownAttribute(attribute.name().into()).into())
}
/// Return a single value for the provided entity and attribute.
@ -415,7 +415,7 @@ where
if !unbound.is_empty() {
// TODO: Allow binding variables at execution time, not just
// preparation time.
bail!(MentatError::UnboundVariables(
bail!(MentatErrorKind::UnboundVariables(
unbound.into_iter().map(|v| v.to_string()).collect()
));
}