Use failure instead of error_chain. (#586) r=nalexander
This commit is contained in:
commit
60a57ea493
71 changed files with 898 additions and 964 deletions
|
@ -28,7 +28,8 @@ rustc_version = "0.2"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
|
failure = "0.1.1"
|
||||||
|
failure_derive = "0.1.1"
|
||||||
lazy_static = "0.2"
|
lazy_static = "0.2"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
uuid = { version = "0.5", features = ["v4", "serde"] }
|
uuid = { version = "0.5", features = ["v4", "serde"] }
|
||||||
|
|
|
@ -6,6 +6,7 @@ workspace = ".."
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
enum-set = { git = "https://github.com/rnewman/enum-set" }
|
enum-set = { git = "https://github.com/rnewman/enum-set" }
|
||||||
|
failure = "0.1.1"
|
||||||
indexmap = "1"
|
indexmap = "1"
|
||||||
lazy_static = "0.2"
|
lazy_static = "0.2"
|
||||||
num = "0.1"
|
num = "0.1"
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
/// Cache traits.
|
/// Cache traits.
|
||||||
|
|
||||||
|
use failure;
|
||||||
|
|
||||||
use std::collections::{
|
use std::collections::{
|
||||||
BTreeSet,
|
BTreeSet,
|
||||||
};
|
};
|
||||||
|
@ -33,8 +35,7 @@ pub trait CachedAttributes {
|
||||||
fn get_entids_for_value(&self, attribute: Entid, value: &TypedValue) -> Option<&BTreeSet<Entid>>;
|
fn get_entids_for_value(&self, attribute: Entid, value: &TypedValue) -> Option<&BTreeSet<Entid>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait UpdateableCache {
|
pub trait UpdateableCache<Error=failure::Error> {
|
||||||
type Error;
|
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<(), Error>
|
||||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<(), Self::Error>
|
|
||||||
where I: Iterator<Item=(Entid, Entid, TypedValue)>;
|
where I: Iterator<Item=(Entid, Entid, TypedValue)>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
extern crate enum_set;
|
extern crate enum_set;
|
||||||
|
extern crate failure;
|
||||||
extern crate indexmap;
|
extern crate indexmap;
|
||||||
extern crate ordered_float;
|
extern crate ordered_float;
|
||||||
extern crate uuid;
|
extern crate uuid;
|
||||||
|
|
|
@ -8,7 +8,8 @@ default = []
|
||||||
sqlcipher = ["rusqlite/sqlcipher"]
|
sqlcipher = ["rusqlite/sqlcipher"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
|
failure = "0.1.1"
|
||||||
|
failure_derive = "0.1.1"
|
||||||
indexmap = "1"
|
indexmap = "1"
|
||||||
itertools = "0.7"
|
itertools = "0.7"
|
||||||
lazy_static = "0.2"
|
lazy_static = "0.2"
|
||||||
|
|
|
@ -11,7 +11,10 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use edn;
|
use edn;
|
||||||
use errors::{ErrorKind, Result};
|
use errors::{
|
||||||
|
DbError,
|
||||||
|
Result,
|
||||||
|
};
|
||||||
use edn::types::Value;
|
use edn::types::Value;
|
||||||
use edn::symbols;
|
use edn::symbols;
|
||||||
use entids;
|
use entids;
|
||||||
|
@ -156,7 +159,7 @@ lazy_static! {
|
||||||
:db/cardinality :db.cardinality/many}}"#;
|
:db/cardinality :db.cardinality/many}}"#;
|
||||||
edn::parse::value(s)
|
edn::parse::value(s)
|
||||||
.map(|v| v.without_spans())
|
.map(|v| v.without_spans())
|
||||||
.map_err(|_| ErrorKind::BadBootstrapDefinition("Unable to parse V1_SYMBOLIC_SCHEMA".into()))
|
.map_err(|_| DbError::BadBootstrapDefinition("Unable to parse V1_SYMBOLIC_SCHEMA".into()))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -207,14 +210,14 @@ fn symbolic_schema_to_triples(ident_map: &IdentMap, symbolic_schema: &Value) ->
|
||||||
for (ident, mp) in m {
|
for (ident, mp) in m {
|
||||||
let ident = match ident {
|
let ident = match ident {
|
||||||
&Value::Keyword(ref ident) => ident,
|
&Value::Keyword(ref ident) => ident,
|
||||||
_ => bail!(ErrorKind::BadBootstrapDefinition(format!("Expected namespaced keyword for ident but got '{:?}'", ident))),
|
_ => bail!(DbError::BadBootstrapDefinition(format!("Expected namespaced keyword for ident but got '{:?}'", ident))),
|
||||||
};
|
};
|
||||||
match *mp {
|
match *mp {
|
||||||
Value::Map(ref mpp) => {
|
Value::Map(ref mpp) => {
|
||||||
for (attr, value) in mpp {
|
for (attr, value) in mpp {
|
||||||
let attr = match attr {
|
let attr = match attr {
|
||||||
&Value::Keyword(ref attr) => attr,
|
&Value::Keyword(ref attr) => attr,
|
||||||
_ => bail!(ErrorKind::BadBootstrapDefinition(format!("Expected namespaced keyword for attr but got '{:?}'", attr))),
|
_ => bail!(DbError::BadBootstrapDefinition(format!("Expected namespaced keyword for attr but got '{:?}'", attr))),
|
||||||
};
|
};
|
||||||
|
|
||||||
// We have symbolic idents but the transactor handles entids. Ad-hoc
|
// We have symbolic idents but the transactor handles entids. Ad-hoc
|
||||||
|
@ -229,20 +232,20 @@ fn symbolic_schema_to_triples(ident_map: &IdentMap, symbolic_schema: &Value) ->
|
||||||
Some(TypedValue::Keyword(ref k)) => {
|
Some(TypedValue::Keyword(ref k)) => {
|
||||||
ident_map.get(k)
|
ident_map.get(k)
|
||||||
.map(|entid| TypedValue::Ref(*entid))
|
.map(|entid| TypedValue::Ref(*entid))
|
||||||
.ok_or(ErrorKind::UnrecognizedIdent(k.to_string()))?
|
.ok_or(DbError::UnrecognizedIdent(k.to_string()))?
|
||||||
},
|
},
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
_ => bail!(ErrorKind::BadBootstrapDefinition(format!("Expected Mentat typed value for value but got '{:?}'", value)))
|
_ => bail!(DbError::BadBootstrapDefinition(format!("Expected Mentat typed value for value but got '{:?}'", value)))
|
||||||
};
|
};
|
||||||
|
|
||||||
triples.push((ident.clone(), attr.clone(), typed_value));
|
triples.push((ident.clone(), attr.clone(), typed_value));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => bail!(ErrorKind::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into()))
|
_ => bail!(DbError::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => bail!(ErrorKind::BadBootstrapDefinition("Expected {...}".into()))
|
_ => bail!(DbError::BadBootstrapDefinition("Expected {...}".into()))
|
||||||
}
|
}
|
||||||
Ok(triples)
|
Ok(triples)
|
||||||
}
|
}
|
||||||
|
@ -263,11 +266,11 @@ fn symbolic_schema_to_assertions(symbolic_schema: &Value) -> Result<Vec<Value>>
|
||||||
value.clone()]));
|
value.clone()]));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => bail!(ErrorKind::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into()))
|
_ => bail!(DbError::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => bail!(ErrorKind::BadBootstrapDefinition("Expected {...}".into()))
|
_ => bail!(DbError::BadBootstrapDefinition("Expected {...}".into()))
|
||||||
}
|
}
|
||||||
Ok(assertions)
|
Ok(assertions)
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ use db::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
DbError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1155,15 +1155,14 @@ impl CachedAttributes for AttributeCaches {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateableCache for AttributeCaches {
|
impl UpdateableCache for AttributeCaches {
|
||||||
type Error = ::errors::Error;
|
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
||||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> ::std::result::Result<(), Self::Error>
|
|
||||||
where I: Iterator<Item=(Entid, Entid, TypedValue)> {
|
where I: Iterator<Item=(Entid, Entid, TypedValue)> {
|
||||||
self.update_with_fallback(None, schema, retractions, assertions)
|
self.update_with_fallback(None, schema, retractions, assertions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttributeCaches {
|
impl AttributeCaches {
|
||||||
fn update_with_fallback<I>(&mut self, fallback: Option<&AttributeCaches>, schema: &Schema, retractions: I, assertions: I) -> ::std::result::Result<(), ::errors::Error>
|
fn update_with_fallback<I>(&mut self, fallback: Option<&AttributeCaches>, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
||||||
where I: Iterator<Item=(Entid, Entid, TypedValue)> {
|
where I: Iterator<Item=(Entid, Entid, TypedValue)> {
|
||||||
let r_aevs = retractions.peekable();
|
let r_aevs = retractions.peekable();
|
||||||
self.accumulate_into_cache(fallback, schema, r_aevs, AccumulationBehavior::Remove)?;
|
self.accumulate_into_cache(fallback, schema, r_aevs, AccumulationBehavior::Remove)?;
|
||||||
|
@ -1237,7 +1236,7 @@ impl SQLiteAttributeCache {
|
||||||
let a = attribute.into();
|
let a = attribute.into();
|
||||||
|
|
||||||
// The attribute must exist!
|
// The attribute must exist!
|
||||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| ErrorKind::UnknownAttribute(a))?;
|
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbError::UnknownAttribute(a))?;
|
||||||
let caches = self.make_mut();
|
let caches = self.make_mut();
|
||||||
caches.forward_cached_attributes.insert(a);
|
caches.forward_cached_attributes.insert(a);
|
||||||
caches.repopulate(schema, sqlite, a)
|
caches.repopulate(schema, sqlite, a)
|
||||||
|
@ -1248,7 +1247,7 @@ impl SQLiteAttributeCache {
|
||||||
let a = attribute.into();
|
let a = attribute.into();
|
||||||
|
|
||||||
// The attribute must exist!
|
// The attribute must exist!
|
||||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| ErrorKind::UnknownAttribute(a))?;
|
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbError::UnknownAttribute(a))?;
|
||||||
|
|
||||||
let caches = self.make_mut();
|
let caches = self.make_mut();
|
||||||
caches.reverse_cached_attributes.insert(a);
|
caches.reverse_cached_attributes.insert(a);
|
||||||
|
@ -1278,8 +1277,7 @@ impl SQLiteAttributeCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateableCache for SQLiteAttributeCache {
|
impl UpdateableCache for SQLiteAttributeCache {
|
||||||
type Error = ::errors::Error;
|
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
||||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> ::std::result::Result<(), Self::Error>
|
|
||||||
where I: Iterator<Item=(Entid, Entid, TypedValue)> {
|
where I: Iterator<Item=(Entid, Entid, TypedValue)> {
|
||||||
self.make_mut().update(schema, retractions, assertions)
|
self.make_mut().update(schema, retractions, assertions)
|
||||||
}
|
}
|
||||||
|
@ -1356,7 +1354,7 @@ impl InProgressSQLiteAttributeCache {
|
||||||
let a = attribute.into();
|
let a = attribute.into();
|
||||||
|
|
||||||
// The attribute must exist!
|
// The attribute must exist!
|
||||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| ErrorKind::UnknownAttribute(a))?;
|
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbError::UnknownAttribute(a))?;
|
||||||
|
|
||||||
if self.is_attribute_cached_forward(a) {
|
if self.is_attribute_cached_forward(a) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -1372,7 +1370,7 @@ impl InProgressSQLiteAttributeCache {
|
||||||
let a = attribute.into();
|
let a = attribute.into();
|
||||||
|
|
||||||
// The attribute must exist!
|
// The attribute must exist!
|
||||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| ErrorKind::UnknownAttribute(a))?;
|
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbError::UnknownAttribute(a))?;
|
||||||
|
|
||||||
if self.is_attribute_cached_reverse(a) {
|
if self.is_attribute_cached_reverse(a) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -1388,7 +1386,7 @@ impl InProgressSQLiteAttributeCache {
|
||||||
let a = attribute.into();
|
let a = attribute.into();
|
||||||
|
|
||||||
// The attribute must exist!
|
// The attribute must exist!
|
||||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| ErrorKind::UnknownAttribute(a))?;
|
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbError::UnknownAttribute(a))?;
|
||||||
|
|
||||||
// TODO: reverse-index unique by default?
|
// TODO: reverse-index unique by default?
|
||||||
let reverse_done = self.is_attribute_cached_reverse(a);
|
let reverse_done = self.is_attribute_cached_reverse(a);
|
||||||
|
@ -1427,8 +1425,7 @@ impl InProgressSQLiteAttributeCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateableCache for InProgressSQLiteAttributeCache {
|
impl UpdateableCache for InProgressSQLiteAttributeCache {
|
||||||
type Error = ::errors::Error;
|
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
||||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> ::std::result::Result<(), Self::Error>
|
|
||||||
where I: Iterator<Item=(Entid, Entid, TypedValue)> {
|
where I: Iterator<Item=(Entid, Entid, TypedValue)> {
|
||||||
self.overlay.update_with_fallback(Some(&self.inner), schema, retractions, assertions)
|
self.overlay.update_with_fallback(Some(&self.inner), schema, retractions, assertions)
|
||||||
}
|
}
|
||||||
|
|
92
db/src/db.rs
92
db/src/db.rs
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use failure::ResultExt;
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::hash_map::{
|
use std::collections::hash_map::{
|
||||||
|
@ -53,7 +55,11 @@ use mentat_core::{
|
||||||
ValueRc,
|
ValueRc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{ErrorKind, Result, ResultExt};
|
use errors::{
|
||||||
|
DbError,
|
||||||
|
Result,
|
||||||
|
DbSqlErrorKind,
|
||||||
|
};
|
||||||
use metadata;
|
use metadata;
|
||||||
use schema::{
|
use schema::{
|
||||||
SchemaBuilding,
|
SchemaBuilding,
|
||||||
|
@ -251,8 +257,8 @@ lazy_static! {
|
||||||
/// documentation](https://www.sqlite.org/pragma.html#pragma_user_version).
|
/// documentation](https://www.sqlite.org/pragma.html#pragma_user_version).
|
||||||
fn set_user_version(conn: &rusqlite::Connection, version: i32) -> Result<()> {
|
fn set_user_version(conn: &rusqlite::Connection, version: i32) -> Result<()> {
|
||||||
conn.execute(&format!("PRAGMA user_version = {}", version), &[])
|
conn.execute(&format!("PRAGMA user_version = {}", version), &[])
|
||||||
.chain_err(|| "Could not set_user_version")
|
.context(DbSqlErrorKind::CouldNotSetVersionPragma)?;
|
||||||
.map(|_| ())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the SQLite user version.
|
/// Get the SQLite user version.
|
||||||
|
@ -260,10 +266,10 @@ fn set_user_version(conn: &rusqlite::Connection, version: i32) -> Result<()> {
|
||||||
/// Mentat manages its own SQL schema version using the user version. See the [SQLite
|
/// Mentat manages its own SQL schema version using the user version. See the [SQLite
|
||||||
/// documentation](https://www.sqlite.org/pragma.html#pragma_user_version).
|
/// documentation](https://www.sqlite.org/pragma.html#pragma_user_version).
|
||||||
fn get_user_version(conn: &rusqlite::Connection) -> Result<i32> {
|
fn get_user_version(conn: &rusqlite::Connection) -> Result<i32> {
|
||||||
conn.query_row("PRAGMA user_version", &[], |row| {
|
let v = conn.query_row("PRAGMA user_version", &[], |row| {
|
||||||
row.get(0)
|
row.get(0)
|
||||||
})
|
}).context(DbSqlErrorKind::CouldNotGetVersionPragma)?;
|
||||||
.chain_err(|| "Could not get_user_version")
|
Ok(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Do just enough work that either `create_current_version` or sync can populate the DB.
|
/// Do just enough work that either `create_current_version` or sync can populate the DB.
|
||||||
|
@ -303,8 +309,7 @@ pub fn create_current_version(conn: &mut rusqlite::Connection) -> Result<DB> {
|
||||||
// TODO: validate metadata mutations that aren't schema related, like additional partitions.
|
// TODO: validate metadata mutations that aren't schema related, like additional partitions.
|
||||||
if let Some(next_schema) = next_schema {
|
if let Some(next_schema) = next_schema {
|
||||||
if next_schema != db.schema {
|
if next_schema != db.schema {
|
||||||
// TODO Use custom ErrorKind https://github.com/brson/error-chain/issues/117
|
bail!(DbError::NotYetImplemented(format!("Initial bootstrap transaction did not produce expected bootstrap schema")));
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("Initial bootstrap transaction did not produce expected bootstrap schema")));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +331,7 @@ pub fn ensure_current_version(conn: &mut rusqlite::Connection) -> Result<DB> {
|
||||||
CURRENT_VERSION => read_db(conn),
|
CURRENT_VERSION => read_db(conn),
|
||||||
|
|
||||||
// TODO: support updating an existing store.
|
// TODO: support updating an existing store.
|
||||||
v => bail!(ErrorKind::NotYetImplemented(format!("Opening databases with Mentat version: {}", v))),
|
v => bail!(DbError::NotYetImplemented(format!("Opening databases with Mentat version: {}", v))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +361,7 @@ impl TypedSQLValue for TypedValue {
|
||||||
let u = Uuid::from_bytes(x.as_slice());
|
let u = Uuid::from_bytes(x.as_slice());
|
||||||
if u.is_err() {
|
if u.is_err() {
|
||||||
// Rather than exposing Uuid's ParseError…
|
// Rather than exposing Uuid's ParseError…
|
||||||
bail!(ErrorKind::BadSQLValuePair(rusqlite::types::Value::Blob(x),
|
bail!(DbError::BadSQLValuePair(rusqlite::types::Value::Blob(x),
|
||||||
value_type_tag));
|
value_type_tag));
|
||||||
}
|
}
|
||||||
Ok(TypedValue::Uuid(u.unwrap()))
|
Ok(TypedValue::Uuid(u.unwrap()))
|
||||||
|
@ -364,7 +369,7 @@ impl TypedSQLValue for TypedValue {
|
||||||
(13, rusqlite::types::Value::Text(x)) => {
|
(13, rusqlite::types::Value::Text(x)) => {
|
||||||
to_namespaced_keyword(&x).map(|k| k.into())
|
to_namespaced_keyword(&x).map(|k| k.into())
|
||||||
},
|
},
|
||||||
(_, value) => bail!(ErrorKind::BadSQLValuePair(value, value_type_tag)),
|
(_, value) => bail!(DbError::BadSQLValuePair(value, value_type_tag)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,12 +452,12 @@ fn read_ident_map(conn: &rusqlite::Connection) -> Result<IdentMap> {
|
||||||
let v = read_materialized_view(conn, "idents")?;
|
let v = read_materialized_view(conn, "idents")?;
|
||||||
v.into_iter().map(|(e, a, typed_value)| {
|
v.into_iter().map(|(e, a, typed_value)| {
|
||||||
if a != entids::DB_IDENT {
|
if a != entids::DB_IDENT {
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("bad idents materialized view: expected :db/ident but got {}", a)));
|
bail!(DbError::NotYetImplemented(format!("bad idents materialized view: expected :db/ident but got {}", a)));
|
||||||
}
|
}
|
||||||
if let TypedValue::Keyword(keyword) = typed_value {
|
if let TypedValue::Keyword(keyword) = typed_value {
|
||||||
Ok((keyword.as_ref().clone(), e))
|
Ok((keyword.as_ref().clone(), e))
|
||||||
} else {
|
} else {
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("bad idents materialized view: expected [entid :db/ident keyword] but got [entid :db/ident {:?}]", typed_value)));
|
bail!(DbError::NotYetImplemented(format!("bad idents materialized view: expected [entid :db/ident keyword] but got [entid :db/ident {:?}]", typed_value)));
|
||||||
}
|
}
|
||||||
}).collect()
|
}).collect()
|
||||||
}
|
}
|
||||||
|
@ -546,9 +551,8 @@ fn search(conn: &rusqlite::Connection) -> Result<()> {
|
||||||
t.a0 = d.a"#;
|
t.a0 = d.a"#;
|
||||||
|
|
||||||
let mut stmt = conn.prepare_cached(s)?;
|
let mut stmt = conn.prepare_cached(s)?;
|
||||||
stmt.execute(&[])
|
stmt.execute(&[]).context(DbSqlErrorKind::CouldNotSearch)?;
|
||||||
.map(|_c| ())
|
Ok(())
|
||||||
.chain_err(|| "Could not search!")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert the new transaction into the `transactions` table.
|
/// Insert the new transaction into the `transactions` table.
|
||||||
|
@ -570,9 +574,7 @@ fn insert_transaction(conn: &rusqlite::Connection, tx: Entid) -> Result<()> {
|
||||||
WHERE added0 IS 1 AND ((rid IS NULL) OR ((rid IS NOT NULL) AND (v0 IS NOT v)))"#;
|
WHERE added0 IS 1 AND ((rid IS NULL) OR ((rid IS NOT NULL) AND (v0 IS NOT v)))"#;
|
||||||
|
|
||||||
let mut stmt = conn.prepare_cached(s)?;
|
let mut stmt = conn.prepare_cached(s)?;
|
||||||
stmt.execute(&[&tx])
|
stmt.execute(&[&tx]).context(DbSqlErrorKind::TxInsertFailedToAddMissingDatoms)?;
|
||||||
.map(|_c| ())
|
|
||||||
.chain_err(|| "Could not insert transaction: failed to add datoms not already present")?;
|
|
||||||
|
|
||||||
let s = r#"
|
let s = r#"
|
||||||
INSERT INTO transactions (e, a, v, tx, added, value_type_tag)
|
INSERT INTO transactions (e, a, v, tx, added, value_type_tag)
|
||||||
|
@ -583,9 +585,7 @@ fn insert_transaction(conn: &rusqlite::Connection, tx: Entid) -> Result<()> {
|
||||||
(added0 IS 1 AND search_type IS ':db.cardinality/one' AND v0 IS NOT v))"#;
|
(added0 IS 1 AND search_type IS ':db.cardinality/one' AND v0 IS NOT v))"#;
|
||||||
|
|
||||||
let mut stmt = conn.prepare_cached(s)?;
|
let mut stmt = conn.prepare_cached(s)?;
|
||||||
stmt.execute(&[&tx])
|
stmt.execute(&[&tx]).context(DbSqlErrorKind::TxInsertFailedToRetractDatoms)?;
|
||||||
.map(|_c| ())
|
|
||||||
.chain_err(|| "Could not insert transaction: failed to retract datoms already present")?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -607,9 +607,7 @@ fn update_datoms(conn: &rusqlite::Connection, tx: Entid) -> Result<()> {
|
||||||
DELETE FROM datoms WHERE rowid IN ids"#;
|
DELETE FROM datoms WHERE rowid IN ids"#;
|
||||||
|
|
||||||
let mut stmt = conn.prepare_cached(s)?;
|
let mut stmt = conn.prepare_cached(s)?;
|
||||||
stmt.execute(&[])
|
stmt.execute(&[]).context(DbSqlErrorKind::DatomsUpdateFailedToRetract)?;
|
||||||
.map(|_c| ())
|
|
||||||
.chain_err(|| "Could not update datoms: failed to retract datoms already present")?;
|
|
||||||
|
|
||||||
// Insert datoms that were added and not already present. We also must expand our bitfield into
|
// Insert datoms that were added and not already present. We also must expand our bitfield into
|
||||||
// flags. Since Mentat follows Datomic and treats its input as a set, it is okay to transact
|
// flags. Since Mentat follows Datomic and treats its input as a set, it is okay to transact
|
||||||
|
@ -632,10 +630,7 @@ fn update_datoms(conn: &rusqlite::Connection, tx: Entid) -> Result<()> {
|
||||||
AttributeBitFlags::UniqueValue as u8);
|
AttributeBitFlags::UniqueValue as u8);
|
||||||
|
|
||||||
let mut stmt = conn.prepare_cached(&s)?;
|
let mut stmt = conn.prepare_cached(&s)?;
|
||||||
stmt.execute(&[&tx])
|
stmt.execute(&[&tx]).context(DbSqlErrorKind::DatomsUpdateFailedToAdd)?;
|
||||||
.map(|_c| ())
|
|
||||||
.chain_err(|| "Could not update datoms: failed to add datoms not already present")?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -762,9 +757,7 @@ impl MentatStoring for rusqlite::Connection {
|
||||||
|
|
||||||
for statement in &statements {
|
for statement in &statements {
|
||||||
let mut stmt = self.prepare_cached(statement)?;
|
let mut stmt = self.prepare_cached(statement)?;
|
||||||
stmt.execute(&[])
|
stmt.execute(&[]).context(DbSqlErrorKind::FailedToCreateTempTables)?;
|
||||||
.map(|_c| ())
|
|
||||||
.chain_err(|| "Failed to create temporary tables")?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -827,8 +820,9 @@ impl MentatStoring for rusqlite::Connection {
|
||||||
// TODO: consider ensuring we inserted the expected number of rows.
|
// TODO: consider ensuring we inserted the expected number of rows.
|
||||||
let mut stmt = self.prepare_cached(s.as_str())?;
|
let mut stmt = self.prepare_cached(s.as_str())?;
|
||||||
stmt.execute(¶ms)
|
stmt.execute(¶ms)
|
||||||
|
.context(DbSqlErrorKind::NonFtsInsertionIntoTempSearchTableFailed)
|
||||||
|
.map_err(|e| e.into())
|
||||||
.map(|_c| ())
|
.map(|_c| ())
|
||||||
.chain_err(|| "Could not insert non-fts one statements into temporary search table!")
|
|
||||||
}).collect::<Result<Vec<()>>>();
|
}).collect::<Result<Vec<()>>>();
|
||||||
|
|
||||||
results.map(|_| ())
|
results.map(|_| ())
|
||||||
|
@ -886,7 +880,7 @@ impl MentatStoring for rusqlite::Connection {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
bail!("Cannot transact a fulltext assertion with a typed value that is not :db/valueType :db.type/string");
|
bail!(DbError::WrongTypeValueForFtsAssertion);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -913,9 +907,7 @@ impl MentatStoring for rusqlite::Connection {
|
||||||
|
|
||||||
// TODO: consider ensuring we inserted the expected number of rows.
|
// TODO: consider ensuring we inserted the expected number of rows.
|
||||||
let mut stmt = self.prepare_cached(fts_s.as_str())?;
|
let mut stmt = self.prepare_cached(fts_s.as_str())?;
|
||||||
stmt.execute(&fts_params)
|
stmt.execute(&fts_params).context(DbSqlErrorKind::FtsInsertionFailed)?;
|
||||||
.map(|_c| ())
|
|
||||||
.chain_err(|| "Could not insert fts values into fts table!")?;
|
|
||||||
|
|
||||||
// Second, insert searches.
|
// Second, insert searches.
|
||||||
// `params` reference computed values in `block`.
|
// `params` reference computed values in `block`.
|
||||||
|
@ -943,17 +935,14 @@ impl MentatStoring for rusqlite::Connection {
|
||||||
|
|
||||||
// TODO: consider ensuring we inserted the expected number of rows.
|
// TODO: consider ensuring we inserted the expected number of rows.
|
||||||
let mut stmt = self.prepare_cached(s.as_str())?;
|
let mut stmt = self.prepare_cached(s.as_str())?;
|
||||||
stmt.execute(¶ms)
|
stmt.execute(¶ms).context(DbSqlErrorKind::FtsInsertionIntoTempSearchTableFailed)
|
||||||
|
.map_err(|e| e.into())
|
||||||
.map(|_c| ())
|
.map(|_c| ())
|
||||||
.chain_err(|| "Could not insert FTS statements into temporary search table!")
|
|
||||||
}).collect::<Result<Vec<()>>>();
|
}).collect::<Result<Vec<()>>>();
|
||||||
|
|
||||||
// Finally, clean up temporary searchids.
|
// Finally, clean up temporary searchids.
|
||||||
let mut stmt = self.prepare_cached("UPDATE fulltext_values SET searchid = NULL WHERE searchid IS NOT NULL")?;
|
let mut stmt = self.prepare_cached("UPDATE fulltext_values SET searchid = NULL WHERE searchid IS NOT NULL")?;
|
||||||
stmt.execute(&[])
|
stmt.execute(&[]).context(DbSqlErrorKind::FtsFailedToDropSearchIds)?;
|
||||||
.map(|_c| ())
|
|
||||||
.chain_err(|| "Could not drop FTS search ids!")?;
|
|
||||||
|
|
||||||
results.map(|_| ())
|
results.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -985,7 +974,7 @@ pub fn update_partition_map(conn: &rusqlite::Connection, partition_map: &Partiti
|
||||||
let max_vars = conn.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER) as usize;
|
let max_vars = conn.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER) as usize;
|
||||||
let max_partitions = max_vars / values_per_statement;
|
let max_partitions = max_vars / values_per_statement;
|
||||||
if partition_map.len() > max_partitions {
|
if partition_map.len() > max_partitions {
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("No more than {} partitions are supported", max_partitions)));
|
bail!(DbError::NotYetImplemented(format!("No more than {} partitions are supported", max_partitions)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Like "UPDATE parts SET idx = CASE WHEN part = ? THEN ? WHEN part = ? THEN ? ELSE idx END".
|
// Like "UPDATE parts SET idx = CASE WHEN part = ? THEN ? WHEN part = ? THEN ? ELSE idx END".
|
||||||
|
@ -1001,9 +990,8 @@ pub fn update_partition_map(conn: &rusqlite::Connection, partition_map: &Partiti
|
||||||
// supported in the Clojure implementation at all, and might not be supported in Mentat soon,
|
// supported in the Clojure implementation at all, and might not be supported in Mentat soon,
|
||||||
// so this is very low priority.
|
// so this is very low priority.
|
||||||
let mut stmt = conn.prepare_cached(s.as_str())?;
|
let mut stmt = conn.prepare_cached(s.as_str())?;
|
||||||
stmt.execute(¶ms[..])
|
stmt.execute(¶ms[..]).context(DbSqlErrorKind::FailedToUpdatePartitionMap)?;
|
||||||
.map(|_c| ())
|
Ok(())
|
||||||
.chain_err(|| "Could not update partition map")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the metadata materialized views based on the given metadata report.
|
/// Update the metadata materialized views based on the given metadata report.
|
||||||
|
@ -1062,8 +1050,8 @@ SELECT EXISTS
|
||||||
// error message in this case.
|
// error message in this case.
|
||||||
if unique_value_stmt.execute(&[to_bool_ref(attribute.unique.is_some()), &entid as &ToSql]).is_err() {
|
if unique_value_stmt.execute(&[to_bool_ref(attribute.unique.is_some()), &entid as &ToSql]).is_err() {
|
||||||
match attribute.unique {
|
match attribute.unique {
|
||||||
Some(attribute::Unique::Value) => bail!(ErrorKind::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.unique/value", entid))),
|
Some(attribute::Unique::Value) => bail!(DbError::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.unique/value", entid))),
|
||||||
Some(attribute::Unique::Identity) => bail!(ErrorKind::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.unique/identity", entid))),
|
Some(attribute::Unique::Identity) => bail!(DbError::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.unique/identity", entid))),
|
||||||
None => unreachable!(), // This shouldn't happen, even after we support removing :db/unique.
|
None => unreachable!(), // This shouldn't happen, even after we support removing :db/unique.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1077,7 +1065,7 @@ SELECT EXISTS
|
||||||
if !attribute.multival {
|
if !attribute.multival {
|
||||||
let mut rows = cardinality_stmt.query(&[&entid as &ToSql])?;
|
let mut rows = cardinality_stmt.query(&[&entid as &ToSql])?;
|
||||||
if rows.next().is_some() {
|
if rows.next().is_some() {
|
||||||
bail!(ErrorKind::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.cardinality/one", entid)));
|
bail!(DbError::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.cardinality/one", entid)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2684,8 +2672,8 @@ mod tests {
|
||||||
|
|
||||||
let report = conn.transact_simple_terms(terms, InternSet::new());
|
let report = conn.transact_simple_terms(terms, InternSet::new());
|
||||||
|
|
||||||
match report.unwrap_err() {
|
match report.unwrap_err().downcast() {
|
||||||
errors::Error(ErrorKind::SchemaConstraintViolation(errors::SchemaConstraintViolation::TypeDisagreements { conflicting_datoms }), _) => {
|
Ok(DbError::SchemaConstraintViolation(errors::SchemaConstraintViolation::TypeDisagreements { conflicting_datoms })) => {
|
||||||
let mut map = BTreeMap::default();
|
let mut map = BTreeMap::default();
|
||||||
map.insert((100, entids::DB_TX_INSTANT, TypedValue::Long(-1)), ValueType::Instant);
|
map.insert((100, entids::DB_TX_INSTANT, TypedValue::Long(-1)), ValueType::Instant);
|
||||||
map.insert((200, entids::DB_IDENT, TypedValue::typed_string("test")), ValueType::Keyword);
|
map.insert((200, entids::DB_IDENT, TypedValue::typed_string("test")), ValueType::Keyword);
|
||||||
|
|
192
db/src/errors.rs
192
db/src/errors.rs
|
@ -10,6 +10,13 @@
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use failure::{
|
||||||
|
Backtrace,
|
||||||
|
Context,
|
||||||
|
Error,
|
||||||
|
Fail,
|
||||||
|
};
|
||||||
|
|
||||||
use std::collections::{
|
use std::collections::{
|
||||||
BTreeMap,
|
BTreeMap,
|
||||||
BTreeSet,
|
BTreeSet,
|
||||||
|
@ -29,6 +36,16 @@ use types::{
|
||||||
ValueType,
|
ValueType,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bail {
|
||||||
|
($e:expr) => (
|
||||||
|
return Err($e.into());
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
// TODO Error/ErrorKind pair
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum CardinalityConflict {
|
pub enum CardinalityConflict {
|
||||||
/// A cardinality one attribute has multiple assertions `[e a v1], [e a v2], ...`.
|
/// A cardinality one attribute has multiple assertions `[e a v1], [e a v2], ...`.
|
||||||
|
@ -46,7 +63,8 @@ pub enum CardinalityConflict {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
// TODO Error/ErrorKind pair
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Fail)]
|
||||||
pub enum SchemaConstraintViolation {
|
pub enum SchemaConstraintViolation {
|
||||||
/// A transaction tried to assert datoms where one tempid upserts to two (or more) distinct
|
/// A transaction tried to assert datoms where one tempid upserts to two (or more) distinct
|
||||||
/// entids.
|
/// entids.
|
||||||
|
@ -125,95 +143,145 @@ impl ::std::fmt::Display for InputError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
error_chain! {
|
#[derive(Debug, Fail)]
|
||||||
types {
|
pub enum DbError {
|
||||||
Error, ErrorKind, ResultExt, Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreign_links {
|
|
||||||
Rusqlite(rusqlite::Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
errors {
|
|
||||||
/// We're just not done yet. Message that the feature is recognized but not yet
|
/// We're just not done yet. Message that the feature is recognized but not yet
|
||||||
/// implemented.
|
/// implemented.
|
||||||
NotYetImplemented(t: String) {
|
#[fail(display = "not yet implemented: {}", _0)]
|
||||||
description("not yet implemented")
|
NotYetImplemented(String),
|
||||||
display("not yet implemented: {}", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We've been given a value that isn't the correct Mentat type.
|
/// We've been given a value that isn't the correct Mentat type.
|
||||||
BadValuePair(value: String, value_type: ValueType) {
|
#[fail(display = "value '{}' is not the expected Mentat value type {:?}", _0, _1)]
|
||||||
description("value is not the expected Mentat value type")
|
BadValuePair(String, ValueType),
|
||||||
display("value '{}' is not the expected Mentat value type {:?}", value, value_type)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We've got corrupt data in the SQL store: a value and value_type_tag don't line up.
|
/// We've got corrupt data in the SQL store: a value and value_type_tag don't line up.
|
||||||
BadSQLValuePair(value: rusqlite::types::Value, value_type_tag: i32) {
|
/// TODO _1.data_type()
|
||||||
description("bad SQL (value_type_tag, value) pair")
|
#[fail(display = "bad SQL (value_type_tag, value) pair: ({:?}, {:?})", _0, _1)]
|
||||||
display("bad SQL (value_type_tag, value) pair: ({}, {:?})", value_type_tag, value.data_type())
|
BadSQLValuePair(rusqlite::types::Value, i32),
|
||||||
}
|
|
||||||
|
|
||||||
// /// The SQLite store user_version isn't recognized. This could be an old version of Mentat
|
// /// The SQLite store user_version isn't recognized. This could be an old version of Mentat
|
||||||
// /// trying to open a newer version SQLite store; or it could be a corrupt file; or ...
|
// /// trying to open a newer version SQLite store; or it could be a corrupt file; or ...
|
||||||
// BadSQLiteStoreVersion(version: i32) {
|
// #[fail(display = "bad SQL store user_version: {}", _0)]
|
||||||
// description("bad SQL store user_version")
|
// BadSQLiteStoreVersion(i32),
|
||||||
// display("bad SQL store user_version: {}", version)
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// A bootstrap definition couldn't be parsed or installed. This is a programmer error, not
|
/// A bootstrap definition couldn't be parsed or installed. This is a programmer error, not
|
||||||
/// a runtime error.
|
/// a runtime error.
|
||||||
BadBootstrapDefinition(t: String) {
|
#[fail(display = "bad bootstrap definition: {}", _0)]
|
||||||
description("bad bootstrap definition")
|
BadBootstrapDefinition(String),
|
||||||
display("bad bootstrap definition: {}", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A schema assertion couldn't be parsed.
|
/// A schema assertion couldn't be parsed.
|
||||||
BadSchemaAssertion(t: String) {
|
#[fail(display = "bad schema assertion: {}", _0)]
|
||||||
description("bad schema assertion")
|
BadSchemaAssertion(String),
|
||||||
display("bad schema assertion: {}", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An ident->entid mapping failed.
|
/// An ident->entid mapping failed.
|
||||||
UnrecognizedIdent(ident: String) {
|
#[fail(display = "no entid found for ident: {}", _0)]
|
||||||
description("no entid found for ident")
|
UnrecognizedIdent(String),
|
||||||
display("no entid found for ident: {}", ident)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An entid->ident mapping failed.
|
/// An entid->ident mapping failed.
|
||||||
/// We also use this error if you try to transact an entid that we didn't allocate,
|
/// We also use this error if you try to transact an entid that we didn't allocate,
|
||||||
/// in part because we blow the stack in error_chain if we define a new enum!
|
/// in part because we blow the stack in error_chain if we define a new enum!
|
||||||
UnrecognizedEntid(entid: Entid) {
|
#[fail(display = "unrecognized or no ident found for entid: {}", _0)]
|
||||||
description("unrecognized or no ident found for entid")
|
UnrecognizedEntid(Entid),
|
||||||
display("unrecognized or no ident found for entid: {}", entid)
|
|
||||||
}
|
|
||||||
|
|
||||||
UnknownAttribute(attr: Entid) {
|
#[fail(display = "unknown attribute for entid: {}", _0)]
|
||||||
description("unknown attribute")
|
UnknownAttribute(Entid),
|
||||||
display("unknown attribute for entid: {}", attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
CannotCacheNonUniqueAttributeInReverse(attr: Entid) {
|
#[fail(display = "cannot reverse-cache non-unique attribute: {}", _0)]
|
||||||
description("cannot reverse-cache non-unique attribute")
|
CannotCacheNonUniqueAttributeInReverse(Entid),
|
||||||
display("cannot reverse-cache non-unique attribute: {}", attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
SchemaAlterationFailed(t: String) {
|
#[fail(display = "schema alteration failed: {}", _0)]
|
||||||
description("schema alteration failed")
|
SchemaAlterationFailed(String),
|
||||||
display("schema alteration failed: {}", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A transaction tried to violate a constraint of the schema of the Mentat store.
|
/// A transaction tried to violate a constraint of the schema of the Mentat store.
|
||||||
SchemaConstraintViolation(violation: SchemaConstraintViolation) {
|
#[fail(display = "schema constraint violation: {}", _0)]
|
||||||
description("schema constraint violation")
|
SchemaConstraintViolation(SchemaConstraintViolation),
|
||||||
display("schema constraint violation: {}", violation)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The transaction was malformed in some way (that was not recognized at parse time; for
|
/// The transaction was malformed in some way (that was not recognized at parse time; for
|
||||||
/// example, in a way that is schema-dependent).
|
/// example, in a way that is schema-dependent).
|
||||||
InputError(error: InputError) {
|
#[fail(display = "transaction input error: {}", _0)]
|
||||||
description("transaction input error")
|
InputError(InputError),
|
||||||
display("transaction input error: {}", error)
|
|
||||||
|
#[fail(display = "Cannot transact a fulltext assertion with a typed value that is not :db/valueType :db.type/string")]
|
||||||
|
WrongTypeValueForFtsAssertion,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DbSqlError {
|
||||||
|
inner: Context<DbSqlErrorKind>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fail for DbSqlError {
|
||||||
|
fn cause(&self) -> Option<&Fail> {
|
||||||
|
self.inner.cause()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backtrace(&self) -> Option<&Backtrace> {
|
||||||
|
self.inner.backtrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Display for DbSqlError {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
|
::std::fmt::Display::fmt(&self.inner, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DbSqlError {
|
||||||
|
pub fn kind(&self) -> DbSqlErrorKind {
|
||||||
|
*self.inner.get_context()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DbSqlErrorKind> for DbSqlError {
|
||||||
|
fn from(kind: DbSqlErrorKind) -> DbSqlError {
|
||||||
|
DbSqlError { inner: Context::new(kind) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Context<DbSqlErrorKind>> for DbSqlError {
|
||||||
|
fn from(inner: Context<DbSqlErrorKind>) -> DbSqlError {
|
||||||
|
DbSqlError { inner: inner }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
|
||||||
|
pub enum DbSqlErrorKind {
|
||||||
|
#[fail(display = "Could not set_user_version")]
|
||||||
|
CouldNotSetVersionPragma,
|
||||||
|
|
||||||
|
#[fail(display = "Could not get_user_version")]
|
||||||
|
CouldNotGetVersionPragma,
|
||||||
|
|
||||||
|
#[fail(display = "Could not search!")]
|
||||||
|
CouldNotSearch,
|
||||||
|
|
||||||
|
#[fail(display = "Could not insert transaction: failed to add datoms not already present")]
|
||||||
|
TxInsertFailedToAddMissingDatoms,
|
||||||
|
|
||||||
|
#[fail(display = "Could not insert transaction: failed to retract datoms already present")]
|
||||||
|
TxInsertFailedToRetractDatoms,
|
||||||
|
|
||||||
|
#[fail(display = "Could not update datoms: failed to retract datoms already present")]
|
||||||
|
DatomsUpdateFailedToRetract,
|
||||||
|
|
||||||
|
#[fail(display = "Could not update datoms: failed to add datoms not already present")]
|
||||||
|
DatomsUpdateFailedToAdd,
|
||||||
|
|
||||||
|
#[fail(display = "Failed to create temporary tables")]
|
||||||
|
FailedToCreateTempTables,
|
||||||
|
|
||||||
|
#[fail(display = "Could not insert non-fts one statements into temporary search table!")]
|
||||||
|
NonFtsInsertionIntoTempSearchTableFailed,
|
||||||
|
|
||||||
|
#[fail(display = "Could not insert fts values into fts table!")]
|
||||||
|
FtsInsertionFailed,
|
||||||
|
|
||||||
|
#[fail(display = "Could not insert FTS statements into temporary search table!")]
|
||||||
|
FtsInsertionIntoTempSearchTableFailed,
|
||||||
|
|
||||||
|
#[fail(display = "Could not drop FTS search ids!")]
|
||||||
|
FtsFailedToDropSearchIds,
|
||||||
|
|
||||||
|
#[fail(display = "Could not update partition map")]
|
||||||
|
FailedToUpdatePartitionMap,
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ use edn::{
|
||||||
|
|
||||||
use errors;
|
use errors;
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
DbError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
use schema::{
|
use schema::{
|
||||||
|
@ -69,7 +69,7 @@ impl TransactableValue for ValueAndSpan {
|
||||||
Ok(EntityPlace::Entid(entities::Entid::Ident(v)))
|
Ok(EntityPlace::Entid(entities::Entid::Ident(v)))
|
||||||
} else {
|
} else {
|
||||||
// We only allow namespaced idents.
|
// We only allow namespaced idents.
|
||||||
bail!(ErrorKind::InputError(errors::InputError::BadEntityPlace))
|
bail!(DbError::InputError(errors::InputError::BadEntityPlace))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Text(v) => Ok(EntityPlace::TempId(TempId::External(v))),
|
Text(v) => Ok(EntityPlace::TempId(TempId::External(v))),
|
||||||
|
@ -86,10 +86,10 @@ impl TransactableValue for ValueAndSpan {
|
||||||
EntityPlace::Entid(a) => Ok(EntityPlace::LookupRef(entities::LookupRef { a: entities::AttributePlace::Entid(a), v: v.clone() })),
|
EntityPlace::Entid(a) => Ok(EntityPlace::LookupRef(entities::LookupRef { a: entities::AttributePlace::Entid(a), v: v.clone() })),
|
||||||
EntityPlace::TempId(_) |
|
EntityPlace::TempId(_) |
|
||||||
EntityPlace::TxFunction(_) |
|
EntityPlace::TxFunction(_) |
|
||||||
EntityPlace::LookupRef(_) => bail!(ErrorKind::InputError(errors::InputError::BadEntityPlace)),
|
EntityPlace::LookupRef(_) => bail!(DbError::InputError(errors::InputError::BadEntityPlace)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => bail!(ErrorKind::InputError(errors::InputError::BadEntityPlace)),
|
_ => bail!(DbError::InputError(errors::InputError::BadEntityPlace)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Nil |
|
Nil |
|
||||||
|
@ -102,7 +102,7 @@ impl TransactableValue for ValueAndSpan {
|
||||||
NamespacedSymbol(_) |
|
NamespacedSymbol(_) |
|
||||||
Vector(_) |
|
Vector(_) |
|
||||||
Set(_) |
|
Set(_) |
|
||||||
Map(_) => bail!(ErrorKind::InputError(errors::InputError::BadEntityPlace)),
|
Map(_) => bail!(DbError::InputError(errors::InputError::BadEntityPlace)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ impl TransactableValue for ValueAndSpan {
|
||||||
impl TransactableValue for TypedValue {
|
impl TransactableValue for TypedValue {
|
||||||
fn into_typed_value(self, _schema: &Schema, value_type: ValueType) -> Result<TypedValue> {
|
fn into_typed_value(self, _schema: &Schema, value_type: ValueType) -> Result<TypedValue> {
|
||||||
if self.value_type() != value_type {
|
if self.value_type() != value_type {
|
||||||
bail!(ErrorKind::BadValuePair(format!("{:?}", self), value_type));
|
bail!(DbError::BadValuePair(format!("{:?}", self), value_type));
|
||||||
}
|
}
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ impl TransactableValue for TypedValue {
|
||||||
TypedValue::Long(_) |
|
TypedValue::Long(_) |
|
||||||
TypedValue::Double(_) |
|
TypedValue::Double(_) |
|
||||||
TypedValue::Instant(_) |
|
TypedValue::Instant(_) |
|
||||||
TypedValue::Uuid(_) => bail!(ErrorKind::InputError(errors::InputError::BadEntityPlace)),
|
TypedValue::Uuid(_) => bail!(DbError::InputError(errors::InputError::BadEntityPlace)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +199,7 @@ pub fn replace_lookup_ref<T, U>(lookup_map: &AVMap, desired_or: Either<T, Lookup
|
||||||
LookupRefOrTempId::LookupRef(av) => lookup_map.get(&*av)
|
LookupRefOrTempId::LookupRef(av) => lookup_map.get(&*av)
|
||||||
.map(|x| lift(*x)).map(Left)
|
.map(|x| lift(*x)).map(Left)
|
||||||
// XXX TODO: fix this error kind!
|
// XXX TODO: fix this error kind!
|
||||||
.ok_or_else(|| ErrorKind::UnrecognizedIdent(format!("couldn't lookup [a v]: {:?}", (*av).clone())).into()),
|
.ok_or_else(|| DbError::UnrecognizedIdent(format!("couldn't lookup [a v]: {:?}", (*av).clone())).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,8 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
// Oh, error_chain.
|
extern crate failure;
|
||||||
#![recursion_limit="128"]
|
#[macro_use] extern crate failure_derive;
|
||||||
|
|
||||||
#[macro_use] extern crate error_chain;
|
|
||||||
extern crate indexmap;
|
extern crate indexmap;
|
||||||
extern crate itertools;
|
extern crate itertools;
|
||||||
#[macro_use] extern crate lazy_static;
|
#[macro_use] extern crate lazy_static;
|
||||||
|
@ -31,7 +29,12 @@ use std::iter::repeat;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
pub use errors::{Error, ErrorKind, ResultExt, Result};
|
pub use errors::{
|
||||||
|
DbError,
|
||||||
|
Result,
|
||||||
|
SchemaConstraintViolation,
|
||||||
|
};
|
||||||
|
#[macro_use] pub mod errors;
|
||||||
|
|
||||||
mod add_retract_alter_set;
|
mod add_retract_alter_set;
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
|
@ -39,7 +42,6 @@ pub mod db;
|
||||||
mod bootstrap;
|
mod bootstrap;
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
pub mod entids;
|
pub mod entids;
|
||||||
pub mod errors;
|
|
||||||
pub mod internal_types; // pub because we need them for building entities programmatically.
|
pub mod internal_types; // pub because we need them for building entities programmatically.
|
||||||
mod metadata;
|
mod metadata;
|
||||||
mod schema;
|
mod schema;
|
||||||
|
@ -114,8 +116,7 @@ pub fn to_namespaced_keyword(s: &str) -> Result<symbols::Keyword> {
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO Use custom ErrorKind https://github.com/brson/error-chain/issues/117
|
nsk.ok_or(DbError::NotYetImplemented(format!("InvalidKeyword: {}", s)).into())
|
||||||
nsk.ok_or(ErrorKind::NotYetImplemented(format!("InvalidKeyword: {}", s)).into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepare an SQL `VALUES` block, like (?, ?, ?), (?, ?, ?).
|
/// Prepare an SQL `VALUES` block, like (?, ?, ?), (?, ?, ?).
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
//!
|
//!
|
||||||
//! This module recognizes, validates, applies, and reports on these mutations.
|
//! This module recognizes, validates, applies, and reports on these mutations.
|
||||||
|
|
||||||
|
use failure::ResultExt;
|
||||||
|
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::collections::btree_map::Entry;
|
use std::collections::btree_map::Entry;
|
||||||
|
|
||||||
|
@ -33,9 +35,8 @@ use add_retract_alter_set::{
|
||||||
use edn::symbols;
|
use edn::symbols;
|
||||||
use entids;
|
use entids;
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
DbError,
|
||||||
Result,
|
Result,
|
||||||
ResultExt,
|
|
||||||
};
|
};
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
attribute,
|
attribute,
|
||||||
|
@ -131,7 +132,7 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
||||||
builder.component(false);
|
builder.component(false);
|
||||||
},
|
},
|
||||||
v => {
|
v => {
|
||||||
bail!(ErrorKind::BadSchemaAssertion(format!("Attempted to retract :db/isComponent with the wrong value {:?}.", v)));
|
bail!(DbError::BadSchemaAssertion(format!("Attempted to retract :db/isComponent with the wrong value {:?}.", v)));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -146,15 +147,15 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
||||||
builder.non_unique();
|
builder.non_unique();
|
||||||
},
|
},
|
||||||
v => {
|
v => {
|
||||||
bail!(ErrorKind::BadSchemaAssertion(format!("Attempted to retract :db/unique with the wrong value {}.", v)));
|
bail!(DbError::BadSchemaAssertion(format!("Attempted to retract :db/unique with the wrong value {}.", v)));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [:db/retract _ :db/unique :db.unique/_] but got [:db/retract {} :db/unique {:?}]", entid, value)))
|
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [:db/retract _ :db/unique :db.unique/_] but got [:db/retract {} :db/unique {:?}]", entid, value)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
bail!(ErrorKind::BadSchemaAssertion(format!("Retracting attribute {} for entity {} not permitted.", attr, entid)));
|
bail!(DbError::BadSchemaAssertion(format!("Retracting attribute {} for entity {} not permitted.", attr, entid)));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,7 +169,7 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
||||||
entids::DB_DOC => {
|
entids::DB_DOC => {
|
||||||
match *value {
|
match *value {
|
||||||
TypedValue::String(_) => {},
|
TypedValue::String(_) => {},
|
||||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/doc \"string value\"] but got [... :db/doc {:?}] for entid {} and attribute {}", value, entid, attr)))
|
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/doc \"string value\"] but got [... :db/doc {:?}] for entid {} and attribute {}", value, entid, attr)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -182,7 +183,7 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
||||||
TypedValue::Ref(entids::DB_TYPE_REF) => { builder.value_type(ValueType::Ref); },
|
TypedValue::Ref(entids::DB_TYPE_REF) => { builder.value_type(ValueType::Ref); },
|
||||||
TypedValue::Ref(entids::DB_TYPE_STRING) => { builder.value_type(ValueType::String); },
|
TypedValue::Ref(entids::DB_TYPE_STRING) => { builder.value_type(ValueType::String); },
|
||||||
TypedValue::Ref(entids::DB_TYPE_UUID) => { builder.value_type(ValueType::Uuid); },
|
TypedValue::Ref(entids::DB_TYPE_UUID) => { builder.value_type(ValueType::Uuid); },
|
||||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/valueType :db.type/*] but got [... :db/valueType {:?}] for entid {} and attribute {}", value, entid, attr)))
|
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/valueType :db.type/*] but got [... :db/valueType {:?}] for entid {} and attribute {}", value, entid, attr)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -190,7 +191,7 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
||||||
match *value {
|
match *value {
|
||||||
TypedValue::Ref(entids::DB_CARDINALITY_MANY) => { builder.multival(true); },
|
TypedValue::Ref(entids::DB_CARDINALITY_MANY) => { builder.multival(true); },
|
||||||
TypedValue::Ref(entids::DB_CARDINALITY_ONE) => { builder.multival(false); },
|
TypedValue::Ref(entids::DB_CARDINALITY_ONE) => { builder.multival(false); },
|
||||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/cardinality :db.cardinality/many|:db.cardinality/one] but got [... :db/cardinality {:?}]", value)))
|
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/cardinality :db.cardinality/many|:db.cardinality/one] but got [... :db/cardinality {:?}]", value)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -198,40 +199,40 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
||||||
match *value {
|
match *value {
|
||||||
TypedValue::Ref(entids::DB_UNIQUE_VALUE) => { builder.unique(attribute::Unique::Value); },
|
TypedValue::Ref(entids::DB_UNIQUE_VALUE) => { builder.unique(attribute::Unique::Value); },
|
||||||
TypedValue::Ref(entids::DB_UNIQUE_IDENTITY) => { builder.unique(attribute::Unique::Identity); },
|
TypedValue::Ref(entids::DB_UNIQUE_IDENTITY) => { builder.unique(attribute::Unique::Identity); },
|
||||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/unique :db.unique/value|:db.unique/identity] but got [... :db/unique {:?}]", value)))
|
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/unique :db.unique/value|:db.unique/identity] but got [... :db/unique {:?}]", value)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
entids::DB_INDEX => {
|
entids::DB_INDEX => {
|
||||||
match *value {
|
match *value {
|
||||||
TypedValue::Boolean(x) => { builder.index(x); },
|
TypedValue::Boolean(x) => { builder.index(x); },
|
||||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/index true|false] but got [... :db/index {:?}]", value)))
|
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/index true|false] but got [... :db/index {:?}]", value)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
entids::DB_FULLTEXT => {
|
entids::DB_FULLTEXT => {
|
||||||
match *value {
|
match *value {
|
||||||
TypedValue::Boolean(x) => { builder.fulltext(x); },
|
TypedValue::Boolean(x) => { builder.fulltext(x); },
|
||||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/fulltext true|false] but got [... :db/fulltext {:?}]", value)))
|
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/fulltext true|false] but got [... :db/fulltext {:?}]", value)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
entids::DB_IS_COMPONENT => {
|
entids::DB_IS_COMPONENT => {
|
||||||
match *value {
|
match *value {
|
||||||
TypedValue::Boolean(x) => { builder.component(x); },
|
TypedValue::Boolean(x) => { builder.component(x); },
|
||||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/isComponent true|false] but got [... :db/isComponent {:?}]", value)))
|
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/isComponent true|false] but got [... :db/isComponent {:?}]", value)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
entids::DB_NO_HISTORY => {
|
entids::DB_NO_HISTORY => {
|
||||||
match *value {
|
match *value {
|
||||||
TypedValue::Boolean(x) => { builder.no_history(x); },
|
TypedValue::Boolean(x) => { builder.no_history(x); },
|
||||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/noHistory true|false] but got [... :db/noHistory {:?}]", value)))
|
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/noHistory true|false] but got [... :db/noHistory {:?}]", value)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
bail!(ErrorKind::BadSchemaAssertion(format!("Do not recognize attribute {} for entid {}", attr, entid)))
|
bail!(DbError::BadSchemaAssertion(format!("Do not recognize attribute {} for entid {}", attr, entid)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -243,8 +244,7 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
||||||
match attribute_map.entry(entid) {
|
match attribute_map.entry(entid) {
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
// Validate once…
|
// Validate once…
|
||||||
builder.validate_install_attribute()
|
builder.validate_install_attribute().context(DbError::BadSchemaAssertion(format!("Schema alteration for new attribute with entid {} is not valid", entid)))?;
|
||||||
.chain_err(|| ErrorKind::BadSchemaAssertion(format!("Schema alteration for new attribute with entid {} is not valid", entid)))?;
|
|
||||||
|
|
||||||
// … and twice, now we have the Attribute.
|
// … and twice, now we have the Attribute.
|
||||||
let a = builder.build();
|
let a = builder.build();
|
||||||
|
@ -254,8 +254,7 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
||||||
},
|
},
|
||||||
|
|
||||||
Entry::Occupied(mut entry) => {
|
Entry::Occupied(mut entry) => {
|
||||||
builder.validate_alter_attribute()
|
builder.validate_alter_attribute().context(DbError::BadSchemaAssertion(format!("Schema alteration for existing attribute with entid {} is not valid", entid)))?;
|
||||||
.chain_err(|| ErrorKind::BadSchemaAssertion(format!("Schema alteration for existing attribute with entid {} is not valid", entid)))?;
|
|
||||||
let mutations = builder.mutate(entry.get_mut());
|
let mutations = builder.mutate(entry.get_mut());
|
||||||
attributes_altered.insert(entid, mutations);
|
attributes_altered.insert(entid, mutations);
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,7 +12,10 @@
|
||||||
|
|
||||||
use db::TypedSQLValue;
|
use db::TypedSQLValue;
|
||||||
use edn;
|
use edn;
|
||||||
use errors::{ErrorKind, Result};
|
use errors::{
|
||||||
|
DbError,
|
||||||
|
Result,
|
||||||
|
};
|
||||||
use edn::symbols;
|
use edn::symbols;
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
attribute,
|
attribute,
|
||||||
|
@ -39,19 +42,19 @@ pub trait AttributeValidation {
|
||||||
impl AttributeValidation for Attribute {
|
impl AttributeValidation for Attribute {
|
||||||
fn validate<F>(&self, ident: F) -> Result<()> where F: Fn() -> String {
|
fn validate<F>(&self, ident: F) -> Result<()> where F: Fn() -> String {
|
||||||
if self.unique == Some(attribute::Unique::Value) && !self.index {
|
if self.unique == Some(attribute::Unique::Value) && !self.index {
|
||||||
bail!(ErrorKind::BadSchemaAssertion(format!(":db/unique :db/unique_value without :db/index true for entid: {}", ident())))
|
bail!(DbError::BadSchemaAssertion(format!(":db/unique :db/unique_value without :db/index true for entid: {}", ident())))
|
||||||
}
|
}
|
||||||
if self.unique == Some(attribute::Unique::Identity) && !self.index {
|
if self.unique == Some(attribute::Unique::Identity) && !self.index {
|
||||||
bail!(ErrorKind::BadSchemaAssertion(format!(":db/unique :db/unique_identity without :db/index true for entid: {}", ident())))
|
bail!(DbError::BadSchemaAssertion(format!(":db/unique :db/unique_identity without :db/index true for entid: {}", ident())))
|
||||||
}
|
}
|
||||||
if self.fulltext && self.value_type != ValueType::String {
|
if self.fulltext && self.value_type != ValueType::String {
|
||||||
bail!(ErrorKind::BadSchemaAssertion(format!(":db/fulltext true without :db/valueType :db.type/string for entid: {}", ident())))
|
bail!(DbError::BadSchemaAssertion(format!(":db/fulltext true without :db/valueType :db.type/string for entid: {}", ident())))
|
||||||
}
|
}
|
||||||
if self.fulltext && !self.index {
|
if self.fulltext && !self.index {
|
||||||
bail!(ErrorKind::BadSchemaAssertion(format!(":db/fulltext true without :db/index true for entid: {}", ident())))
|
bail!(DbError::BadSchemaAssertion(format!(":db/fulltext true without :db/index true for entid: {}", ident())))
|
||||||
}
|
}
|
||||||
if self.component && self.value_type != ValueType::Ref {
|
if self.component && self.value_type != ValueType::Ref {
|
||||||
bail!(ErrorKind::BadSchemaAssertion(format!(":db/isComponent true without :db/valueType :db.type/ref for entid: {}", ident())))
|
bail!(DbError::BadSchemaAssertion(format!(":db/isComponent true without :db/valueType :db.type/ref for entid: {}", ident())))
|
||||||
}
|
}
|
||||||
// TODO: consider warning if we have :db/index true for :db/valueType :db.type/string,
|
// TODO: consider warning if we have :db/index true for :db/valueType :db.type/string,
|
||||||
// since this may be inefficient. More generally, we should try to drive complex
|
// since this may be inefficient. More generally, we should try to drive complex
|
||||||
|
@ -150,17 +153,17 @@ impl AttributeBuilder {
|
||||||
|
|
||||||
pub fn validate_install_attribute(&self) -> Result<()> {
|
pub fn validate_install_attribute(&self) -> Result<()> {
|
||||||
if self.value_type.is_none() {
|
if self.value_type.is_none() {
|
||||||
bail!(ErrorKind::BadSchemaAssertion("Schema attribute for new attribute does not set :db/valueType".into()));
|
bail!(DbError::BadSchemaAssertion("Schema attribute for new attribute does not set :db/valueType".into()));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_alter_attribute(&self) -> Result<()> {
|
pub fn validate_alter_attribute(&self) -> Result<()> {
|
||||||
if self.value_type.is_some() {
|
if self.value_type.is_some() {
|
||||||
bail!(ErrorKind::BadSchemaAssertion("Schema alteration must not set :db/valueType".into()));
|
bail!(DbError::BadSchemaAssertion("Schema alteration must not set :db/valueType".into()));
|
||||||
}
|
}
|
||||||
if self.fulltext.is_some() {
|
if self.fulltext.is_some() {
|
||||||
bail!(ErrorKind::BadSchemaAssertion("Schema alteration must not set :db/fulltext".into()));
|
bail!(DbError::BadSchemaAssertion("Schema alteration must not set :db/fulltext".into()));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -247,15 +250,15 @@ pub trait SchemaBuilding {
|
||||||
|
|
||||||
impl SchemaBuilding for Schema {
|
impl SchemaBuilding for Schema {
|
||||||
fn require_ident(&self, entid: Entid) -> Result<&symbols::Keyword> {
|
fn require_ident(&self, entid: Entid) -> Result<&symbols::Keyword> {
|
||||||
self.get_ident(entid).ok_or(ErrorKind::UnrecognizedEntid(entid).into())
|
self.get_ident(entid).ok_or(DbError::UnrecognizedEntid(entid).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn require_entid(&self, ident: &symbols::Keyword) -> Result<KnownEntid> {
|
fn require_entid(&self, ident: &symbols::Keyword) -> Result<KnownEntid> {
|
||||||
self.get_entid(&ident).ok_or(ErrorKind::UnrecognizedIdent(ident.to_string()).into())
|
self.get_entid(&ident).ok_or(DbError::UnrecognizedIdent(ident.to_string()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn require_attribute_for_entid(&self, entid: Entid) -> Result<&Attribute> {
|
fn require_attribute_for_entid(&self, entid: Entid) -> Result<&Attribute> {
|
||||||
self.attribute_for_entid(entid).ok_or(ErrorKind::UnrecognizedEntid(entid).into())
|
self.attribute_for_entid(entid).ok_or(DbError::UnrecognizedEntid(entid).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a valid `Schema` from the constituent maps.
|
/// Create a valid `Schema` from the constituent maps.
|
||||||
|
@ -271,8 +274,8 @@ impl SchemaBuilding for Schema {
|
||||||
where U: IntoIterator<Item=(symbols::Keyword, symbols::Keyword, TypedValue)>{
|
where U: IntoIterator<Item=(symbols::Keyword, symbols::Keyword, TypedValue)>{
|
||||||
|
|
||||||
let entid_assertions: Result<Vec<(Entid, Entid, TypedValue)>> = assertions.into_iter().map(|(symbolic_ident, symbolic_attr, value)| {
|
let entid_assertions: Result<Vec<(Entid, Entid, TypedValue)>> = assertions.into_iter().map(|(symbolic_ident, symbolic_attr, value)| {
|
||||||
let ident: i64 = *ident_map.get(&symbolic_ident).ok_or(ErrorKind::UnrecognizedIdent(symbolic_ident.to_string()))?;
|
let ident: i64 = *ident_map.get(&symbolic_ident).ok_or(DbError::UnrecognizedIdent(symbolic_ident.to_string()))?;
|
||||||
let attr: i64 = *ident_map.get(&symbolic_attr).ok_or(ErrorKind::UnrecognizedIdent(symbolic_attr.to_string()))?;
|
let attr: i64 = *ident_map.get(&symbolic_attr).ok_or(DbError::UnrecognizedIdent(symbolic_attr.to_string()))?;
|
||||||
Ok((ident, attr, value))
|
Ok((ident, attr, value))
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
|
@ -305,7 +308,7 @@ impl SchemaTypeChecking for Schema {
|
||||||
// wrapper function.
|
// wrapper function.
|
||||||
match TypedValue::from_edn_value(&value.clone().without_spans()) {
|
match TypedValue::from_edn_value(&value.clone().without_spans()) {
|
||||||
// We don't recognize this EDN at all. Get out!
|
// We don't recognize this EDN at all. Get out!
|
||||||
None => bail!(ErrorKind::BadValuePair(format!("{}", value), value_type)),
|
None => bail!(DbError::BadValuePair(format!("{}", value), value_type)),
|
||||||
Some(typed_value) => match (value_type, typed_value) {
|
Some(typed_value) => match (value_type, typed_value) {
|
||||||
// Most types don't coerce at all.
|
// Most types don't coerce at all.
|
||||||
(ValueType::Boolean, tv @ TypedValue::Boolean(_)) => Ok(tv),
|
(ValueType::Boolean, tv @ TypedValue::Boolean(_)) => Ok(tv),
|
||||||
|
@ -331,7 +334,7 @@ impl SchemaTypeChecking for Schema {
|
||||||
(vt @ ValueType::Instant, _) |
|
(vt @ ValueType::Instant, _) |
|
||||||
(vt @ ValueType::Keyword, _) |
|
(vt @ ValueType::Keyword, _) |
|
||||||
(vt @ ValueType::Ref, _)
|
(vt @ ValueType::Ref, _)
|
||||||
=> bail!(ErrorKind::BadValuePair(format!("{}", value), vt)),
|
=> bail!(DbError::BadValuePair(format!("{}", value), vt)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -343,7 +346,6 @@ impl SchemaTypeChecking for Schema {
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use self::edn::Keyword;
|
use self::edn::Keyword;
|
||||||
use errors::Error;
|
|
||||||
|
|
||||||
fn add_attribute(schema: &mut Schema,
|
fn add_attribute(schema: &mut Schema,
|
||||||
ident: Keyword,
|
ident: Keyword,
|
||||||
|
@ -435,8 +437,8 @@ mod test {
|
||||||
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
||||||
assert!(err.is_some());
|
assert!(err.is_some());
|
||||||
|
|
||||||
match err.unwrap() {
|
match err.unwrap().downcast() {
|
||||||
Error(ErrorKind::BadSchemaAssertion(message), _) => { assert_eq!(message, ":db/unique :db/unique_value without :db/index true for entid: :foo/bar"); },
|
Ok(DbError::BadSchemaAssertion(message)) => { assert_eq!(message, ":db/unique :db/unique_value without :db/index true for entid: :foo/bar"); },
|
||||||
x => panic!("expected Bad Schema Assertion error, got {:?}", x),
|
x => panic!("expected Bad Schema Assertion error, got {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -458,8 +460,8 @@ mod test {
|
||||||
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
||||||
assert!(err.is_some());
|
assert!(err.is_some());
|
||||||
|
|
||||||
match err.unwrap() {
|
match err.unwrap().downcast() {
|
||||||
Error(ErrorKind::BadSchemaAssertion(message), _) => { assert_eq!(message, ":db/unique :db/unique_identity without :db/index true for entid: :foo/bar"); },
|
Ok(DbError::BadSchemaAssertion(message)) => { assert_eq!(message, ":db/unique :db/unique_identity without :db/index true for entid: :foo/bar"); },
|
||||||
x => panic!("expected Bad Schema Assertion error, got {:?}", x),
|
x => panic!("expected Bad Schema Assertion error, got {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -481,8 +483,8 @@ mod test {
|
||||||
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
||||||
assert!(err.is_some());
|
assert!(err.is_some());
|
||||||
|
|
||||||
match err.unwrap() {
|
match err.unwrap().downcast() {
|
||||||
Error(ErrorKind::BadSchemaAssertion(message), _) => { assert_eq!(message, ":db/isComponent true without :db/valueType :db.type/ref for entid: :foo/bar"); },
|
Ok(DbError::BadSchemaAssertion(message)) => { assert_eq!(message, ":db/isComponent true without :db/valueType :db.type/ref for entid: :foo/bar"); },
|
||||||
x => panic!("expected Bad Schema Assertion error, got {:?}", x),
|
x => panic!("expected Bad Schema Assertion error, got {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -504,8 +506,8 @@ mod test {
|
||||||
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
||||||
assert!(err.is_some());
|
assert!(err.is_some());
|
||||||
|
|
||||||
match err.unwrap() {
|
match err.unwrap().downcast() {
|
||||||
Error(ErrorKind::BadSchemaAssertion(message), _) => { assert_eq!(message, ":db/fulltext true without :db/index true for entid: :foo/bar"); },
|
Ok(DbError::BadSchemaAssertion(message)) => { assert_eq!(message, ":db/fulltext true without :db/index true for entid: :foo/bar"); },
|
||||||
x => panic!("expected Bad Schema Assertion error, got {:?}", x),
|
x => panic!("expected Bad Schema Assertion error, got {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -526,8 +528,8 @@ mod test {
|
||||||
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
||||||
assert!(err.is_some());
|
assert!(err.is_some());
|
||||||
|
|
||||||
match err.unwrap() {
|
match err.unwrap().downcast() {
|
||||||
Error(ErrorKind::BadSchemaAssertion(message), _) => { assert_eq!(message, ":db/fulltext true without :db/valueType :db.type/string for entid: :foo/bar"); },
|
Ok(DbError::BadSchemaAssertion(message)) => { assert_eq!(message, ":db/fulltext true without :db/valueType :db.type/string for entid: :foo/bar"); },
|
||||||
x => panic!("expected Bad Schema Assertion error, got {:?}", x),
|
x => panic!("expected Bad Schema Assertion error, got {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
44
db/src/tx.rs
44
db/src/tx.rs
|
@ -71,7 +71,7 @@ use edn::{
|
||||||
use entids;
|
use entids;
|
||||||
use errors;
|
use errors;
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
DbError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
use internal_types::{
|
use internal_types::{
|
||||||
|
@ -179,7 +179,7 @@ pub fn remove_db_id<V: TransactableValue>(map: &mut entmod::MapNotation<V>) -> R
|
||||||
entmod::ValuePlace::Atom(v) => Some(v.into_entity_place()?),
|
entmod::ValuePlace::Atom(v) => Some(v.into_entity_place()?),
|
||||||
entmod::ValuePlace::Vector(_) |
|
entmod::ValuePlace::Vector(_) |
|
||||||
entmod::ValuePlace::MapNotation(_) => {
|
entmod::ValuePlace::MapNotation(_) => {
|
||||||
bail!(ErrorKind::InputError(errors::InputError::BadDbId))
|
bail!(DbError::InputError(errors::InputError::BadDbId))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -244,7 +244,7 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !conflicting_upserts.is_empty() {
|
if !conflicting_upserts.is_empty() {
|
||||||
bail!(ErrorKind::SchemaConstraintViolation(errors::SchemaConstraintViolation::ConflictingUpserts { conflicting_upserts }));
|
bail!(DbError::SchemaConstraintViolation(errors::SchemaConstraintViolation::ConflictingUpserts { conflicting_upserts }));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(tempids)
|
Ok(tempids)
|
||||||
|
@ -281,7 +281,7 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
if self.partition_map.contains_entid(e) {
|
if self.partition_map.contains_entid(e) {
|
||||||
Ok(KnownEntid(e))
|
Ok(KnownEntid(e))
|
||||||
} else {
|
} else {
|
||||||
bail!(ErrorKind::UnrecognizedEntid(e))
|
bail!(DbError::UnrecognizedEntid(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +298,7 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
|
|
||||||
let lr_typed_value: TypedValue = lookup_ref.v.clone().into_typed_value(&self.schema, lr_attribute.value_type)?;
|
let lr_typed_value: TypedValue = lookup_ref.v.clone().into_typed_value(&self.schema, lr_attribute.value_type)?;
|
||||||
if lr_attribute.unique.is_none() {
|
if lr_attribute.unique.is_none() {
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("Cannot resolve (lookup-ref {} {:?}) with attribute that is not :db/unique", lr_a, lr_typed_value)))
|
bail!(DbError::NotYetImplemented(format!("Cannot resolve (lookup-ref {} {:?}) with attribute that is not :db/unique", lr_a, lr_typed_value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self.lookup_refs.intern((lr_a, lr_typed_value)))
|
Ok(self.lookup_refs.intern((lr_a, lr_typed_value)))
|
||||||
|
@ -336,7 +336,7 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
entmod::EntityPlace::TxFunction(ref tx_function) => {
|
entmod::EntityPlace::TxFunction(ref tx_function) => {
|
||||||
match tx_function.op.0.as_str() {
|
match tx_function.op.0.as_str() {
|
||||||
"transaction-tx" => Ok(Either::Left(self.tx_id)),
|
"transaction-tx" => Ok(Either::Left(self.tx_id)),
|
||||||
unknown @ _ => bail!(ErrorKind::NotYetImplemented(format!("Unknown transaction function {}", unknown))),
|
unknown @ _ => bail!(DbError::NotYetImplemented(format!("Unknown transaction function {}", unknown))),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -357,13 +357,13 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
fn entity_v_into_term_e<W: TransactableValue>(&mut self, x: entmod::ValuePlace<W>, backward_a: &entmod::Entid) -> Result<KnownEntidOr<LookupRefOrTempId>> {
|
fn entity_v_into_term_e<W: TransactableValue>(&mut self, x: entmod::ValuePlace<W>, backward_a: &entmod::Entid) -> Result<KnownEntidOr<LookupRefOrTempId>> {
|
||||||
match backward_a.unreversed() {
|
match backward_a.unreversed() {
|
||||||
None => {
|
None => {
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("Cannot explode map notation value in :attr/_reversed notation for forward attribute")));
|
bail!(DbError::NotYetImplemented(format!("Cannot explode map notation value in :attr/_reversed notation for forward attribute")));
|
||||||
},
|
},
|
||||||
Some(forward_a) => {
|
Some(forward_a) => {
|
||||||
let forward_a = self.entity_a_into_term_a(forward_a)?;
|
let forward_a = self.entity_a_into_term_a(forward_a)?;
|
||||||
let forward_attribute = self.schema.require_attribute_for_entid(forward_a)?;
|
let forward_attribute = self.schema.require_attribute_for_entid(forward_a)?;
|
||||||
if forward_attribute.value_type != ValueType::Ref {
|
if forward_attribute.value_type != ValueType::Ref {
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("Cannot use :attr/_reversed notation for attribute {} that is not :db/valueType :db.type/ref", forward_a)))
|
bail!(DbError::NotYetImplemented(format!("Cannot use :attr/_reversed notation for attribute {} that is not :db/valueType :db.type/ref", forward_a)))
|
||||||
}
|
}
|
||||||
|
|
||||||
match x {
|
match x {
|
||||||
|
@ -378,7 +378,7 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
Ok(Either::Left(KnownEntid(entid)))
|
Ok(Either::Left(KnownEntid(entid)))
|
||||||
} else {
|
} else {
|
||||||
// The given value is expected to be :db.type/ref, so this shouldn't happen.
|
// The given value is expected to be :db.type/ref, so this shouldn't happen.
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("Cannot use :attr/_reversed notation for attribute {} with value that is not :db.valueType :db.type/ref", forward_a)))
|
bail!(DbError::NotYetImplemented(format!("Cannot use :attr/_reversed notation for attribute {} with value that is not :db.valueType :db.type/ref", forward_a)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -396,15 +396,15 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
entmod::ValuePlace::TxFunction(ref tx_function) => {
|
entmod::ValuePlace::TxFunction(ref tx_function) => {
|
||||||
match tx_function.op.0.as_str() {
|
match tx_function.op.0.as_str() {
|
||||||
"transaction-tx" => Ok(Either::Left(KnownEntid(self.tx_id.0))),
|
"transaction-tx" => Ok(Either::Left(KnownEntid(self.tx_id.0))),
|
||||||
unknown @ _ => bail!(ErrorKind::NotYetImplemented(format!("Unknown transaction function {}", unknown))),
|
unknown @ _ => bail!(DbError::NotYetImplemented(format!("Unknown transaction function {}", unknown))),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
entmod::ValuePlace::Vector(_) =>
|
entmod::ValuePlace::Vector(_) =>
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("Cannot explode vector value in :attr/_reversed notation for attribute {}", forward_a))),
|
bail!(DbError::NotYetImplemented(format!("Cannot explode vector value in :attr/_reversed notation for attribute {}", forward_a))),
|
||||||
|
|
||||||
entmod::ValuePlace::MapNotation(_) =>
|
entmod::ValuePlace::MapNotation(_) =>
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("Cannot explode map notation value in :attr/_reversed notation for attribute {}", forward_a))),
|
bail!(DbError::NotYetImplemented(format!("Cannot explode map notation value in :attr/_reversed notation for attribute {}", forward_a))),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -475,7 +475,7 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
|
|
||||||
entmod::ValuePlace::LookupRef(ref lookup_ref) => {
|
entmod::ValuePlace::LookupRef(ref lookup_ref) => {
|
||||||
if attribute.value_type != ValueType::Ref {
|
if attribute.value_type != ValueType::Ref {
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("Cannot resolve value lookup ref for attribute {} that is not :db/valueType :db.type/ref", a)))
|
bail!(DbError::NotYetImplemented(format!("Cannot resolve value lookup ref for attribute {} that is not :db/valueType :db.type/ref", a)))
|
||||||
}
|
}
|
||||||
|
|
||||||
Either::Right(LookupRefOrTempId::LookupRef(in_process.intern_lookup_ref(lookup_ref)?))
|
Either::Right(LookupRefOrTempId::LookupRef(in_process.intern_lookup_ref(lookup_ref)?))
|
||||||
|
@ -484,7 +484,7 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
entmod::ValuePlace::TxFunction(ref tx_function) => {
|
entmod::ValuePlace::TxFunction(ref tx_function) => {
|
||||||
let typed_value = match tx_function.op.0.as_str() {
|
let typed_value = match tx_function.op.0.as_str() {
|
||||||
"transaction-tx" => TypedValue::Ref(self.tx_id),
|
"transaction-tx" => TypedValue::Ref(self.tx_id),
|
||||||
unknown @ _ => bail!(ErrorKind::NotYetImplemented(format!("Unknown transaction function {}", unknown))),
|
unknown @ _ => bail!(DbError::NotYetImplemented(format!("Unknown transaction function {}", unknown))),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Here we do schema-aware typechecking: we assert that the computed
|
// Here we do schema-aware typechecking: we assert that the computed
|
||||||
|
@ -494,7 +494,7 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
// value can be used where a double is expected. See also
|
// value can be used where a double is expected. See also
|
||||||
// `SchemaTypeChecking.to_typed_value(...)`.
|
// `SchemaTypeChecking.to_typed_value(...)`.
|
||||||
if attribute.value_type != typed_value.value_type() {
|
if attribute.value_type != typed_value.value_type() {
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("Transaction function {} produced value of type {} but expected type {}",
|
bail!(DbError::NotYetImplemented(format!("Transaction function {} produced value of type {} but expected type {}",
|
||||||
tx_function.op.0.as_str(), typed_value.value_type(), attribute.value_type)));
|
tx_function.op.0.as_str(), typed_value.value_type(), attribute.value_type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,7 +503,7 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
|
|
||||||
entmod::ValuePlace::Vector(vs) => {
|
entmod::ValuePlace::Vector(vs) => {
|
||||||
if !attribute.multival {
|
if !attribute.multival {
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("Cannot explode vector value for attribute {} that is not :db.cardinality :db.cardinality/many", a)));
|
bail!(DbError::NotYetImplemented(format!("Cannot explode vector value for attribute {} that is not :db.cardinality :db.cardinality/many", a)));
|
||||||
}
|
}
|
||||||
|
|
||||||
for vv in vs {
|
for vv in vs {
|
||||||
|
@ -523,11 +523,11 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
// AddOrRetract, which proliferates types and code, or only handling
|
// AddOrRetract, which proliferates types and code, or only handling
|
||||||
// nested maps rather than map values, like Datomic does.
|
// nested maps rather than map values, like Datomic does.
|
||||||
if op != OpType::Add {
|
if op != OpType::Add {
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("Cannot explode nested map value in :db/retract for attribute {}", a)));
|
bail!(DbError::NotYetImplemented(format!("Cannot explode nested map value in :db/retract for attribute {}", a)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if attribute.value_type != ValueType::Ref {
|
if attribute.value_type != ValueType::Ref {
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("Cannot explode nested map value for attribute {} that is not :db/valueType :db.type/ref", a)))
|
bail!(DbError::NotYetImplemented(format!("Cannot explode nested map value for attribute {} that is not :db/valueType :db.type/ref", a)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// :db/id is optional; if it's not given, we generate a special internal tempid
|
// :db/id is optional; if it's not given, we generate a special internal tempid
|
||||||
|
@ -580,7 +580,7 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
if dangling {
|
if dangling {
|
||||||
bail!(ErrorKind::NotYetImplemented(format!("Cannot explode nested map value that would lead to dangling entity for attribute {}", a)));
|
bail!(DbError::NotYetImplemented(format!("Cannot explode nested map value that would lead to dangling entity for attribute {}", a)));
|
||||||
}
|
}
|
||||||
|
|
||||||
in_process.entity_e_into_term_v(db_id)?
|
in_process.entity_e_into_term_v(db_id)?
|
||||||
|
@ -668,7 +668,7 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !conflicting_upserts.is_empty() {
|
if !conflicting_upserts.is_empty() {
|
||||||
bail!(ErrorKind::SchemaConstraintViolation(errors::SchemaConstraintViolation::ConflictingUpserts { conflicting_upserts }));
|
bail!(DbError::SchemaConstraintViolation(errors::SchemaConstraintViolation::ConflictingUpserts { conflicting_upserts }));
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("tempids {:?}", tempids);
|
debug!("tempids {:?}", tempids);
|
||||||
|
@ -742,12 +742,12 @@ impl<'conn, 'a, W> Tx<'conn, 'a, W> where W: TransactWatcher {
|
||||||
|
|
||||||
let errors = tx_checking::type_disagreements(&aev_trie);
|
let errors = tx_checking::type_disagreements(&aev_trie);
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
bail!(ErrorKind::SchemaConstraintViolation(errors::SchemaConstraintViolation::TypeDisagreements { conflicting_datoms: errors }));
|
bail!(DbError::SchemaConstraintViolation(errors::SchemaConstraintViolation::TypeDisagreements { conflicting_datoms: errors }));
|
||||||
}
|
}
|
||||||
|
|
||||||
let errors = tx_checking::cardinality_conflicts(&aev_trie);
|
let errors = tx_checking::cardinality_conflicts(&aev_trie);
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
bail!(ErrorKind::SchemaConstraintViolation(errors::SchemaConstraintViolation::CardinalityConflicts { conflicts: errors }));
|
bail!(DbError::SchemaConstraintViolation(errors::SchemaConstraintViolation::CardinalityConflicts { conflicts: errors }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pipeline stage 4: final terms (after rewriting) -> DB insertions.
|
// Pipeline stage 4: final terms (after rewriting) -> DB insertions.
|
||||||
|
|
|
@ -21,8 +21,10 @@ use std::collections::{
|
||||||
use indexmap;
|
use indexmap;
|
||||||
use petgraph::unionfind;
|
use petgraph::unionfind;
|
||||||
|
|
||||||
use errors;
|
use errors::{
|
||||||
use errors::ErrorKind;
|
DbError,
|
||||||
|
Result,
|
||||||
|
};
|
||||||
use types::{
|
use types::{
|
||||||
AVPair,
|
AVPair,
|
||||||
};
|
};
|
||||||
|
@ -100,11 +102,11 @@ pub(crate) struct FinalPopulations {
|
||||||
impl Generation {
|
impl Generation {
|
||||||
/// Split entities into a generation of populations that need to evolve to have their tempids
|
/// Split entities into a generation of populations that need to evolve to have their tempids
|
||||||
/// resolved or allocated, and a population of inert entities that do not reference tempids.
|
/// resolved or allocated, and a population of inert entities that do not reference tempids.
|
||||||
pub(crate) fn from<I>(terms: I, schema: &Schema) -> errors::Result<(Generation, Population)> where I: IntoIterator<Item=TermWithTempIds> {
|
pub(crate) fn from<I>(terms: I, schema: &Schema) -> Result<(Generation, Population)> where I: IntoIterator<Item=TermWithTempIds> {
|
||||||
let mut generation = Generation::default();
|
let mut generation = Generation::default();
|
||||||
let mut inert = vec![];
|
let mut inert = vec![];
|
||||||
|
|
||||||
let is_unique = |a: Entid| -> errors::Result<bool> {
|
let is_unique = |a: Entid| -> Result<bool> {
|
||||||
let attribute: &Attribute = schema.require_attribute_for_entid(a)?;
|
let attribute: &Attribute = schema.require_attribute_for_entid(a)?;
|
||||||
Ok(attribute.unique == Some(attribute::Unique::Identity))
|
Ok(attribute.unique == Some(attribute::Unique::Identity))
|
||||||
};
|
};
|
||||||
|
@ -223,7 +225,7 @@ impl Generation {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evolve potential upserts that haven't resolved into allocations.
|
/// Evolve potential upserts that haven't resolved into allocations.
|
||||||
pub(crate) fn allocate_unresolved_upserts(&mut self) -> errors::Result<()> {
|
pub(crate) fn allocate_unresolved_upserts(&mut self) -> Result<()> {
|
||||||
let mut upserts_ev = vec![];
|
let mut upserts_ev = vec![];
|
||||||
::std::mem::swap(&mut self.upserts_ev, &mut upserts_ev);
|
::std::mem::swap(&mut self.upserts_ev, &mut upserts_ev);
|
||||||
|
|
||||||
|
@ -236,7 +238,7 @@ impl Generation {
|
||||||
///
|
///
|
||||||
/// Some of the tempids may be identified, so we also provide a map from tempid to a dense set
|
/// Some of the tempids may be identified, so we also provide a map from tempid to a dense set
|
||||||
/// of contiguous integer labels.
|
/// of contiguous integer labels.
|
||||||
pub(crate) fn temp_ids_in_allocations(&self, schema: &Schema) -> errors::Result<BTreeMap<TempIdHandle, usize>> {
|
pub(crate) fn temp_ids_in_allocations(&self, schema: &Schema) -> Result<BTreeMap<TempIdHandle, usize>> {
|
||||||
assert!(self.upserts_e.is_empty(), "All upserts should have been upserted, resolved, or moved to the allocated population!");
|
assert!(self.upserts_e.is_empty(), "All upserts should have been upserted, resolved, or moved to the allocated population!");
|
||||||
assert!(self.upserts_ev.is_empty(), "All upserts should have been upserted, resolved, or moved to the allocated population!");
|
assert!(self.upserts_ev.is_empty(), "All upserts should have been upserted, resolved, or moved to the allocated population!");
|
||||||
|
|
||||||
|
@ -313,7 +315,7 @@ impl Generation {
|
||||||
|
|
||||||
/// After evolution is complete, use the provided allocated entids to segment `self` into
|
/// After evolution is complete, use the provided allocated entids to segment `self` into
|
||||||
/// populations, each with no references to tempids.
|
/// populations, each with no references to tempids.
|
||||||
pub(crate) fn into_final_populations(self, temp_id_map: &TempIdMap) -> errors::Result<FinalPopulations> {
|
pub(crate) fn into_final_populations(self, temp_id_map: &TempIdMap) -> Result<FinalPopulations> {
|
||||||
assert!(self.upserts_e.is_empty());
|
assert!(self.upserts_e.is_empty());
|
||||||
assert!(self.upserts_ev.is_empty());
|
assert!(self.upserts_ev.is_empty());
|
||||||
|
|
||||||
|
@ -329,21 +331,21 @@ impl Generation {
|
||||||
match (op, temp_id_map.get(&*t1), temp_id_map.get(&*t2)) {
|
match (op, temp_id_map.get(&*t1), temp_id_map.get(&*t2)) {
|
||||||
(op, Some(&n1), Some(&n2)) => Term::AddOrRetract(op, n1, a, TypedValue::Ref(n2.0)),
|
(op, Some(&n1), Some(&n2)) => Term::AddOrRetract(op, n1, a, TypedValue::Ref(n2.0)),
|
||||||
(OpType::Add, _, _) => unreachable!(), // This is a coding error -- every tempid in a :db/add entity should resolve or be allocated.
|
(OpType::Add, _, _) => unreachable!(), // This is a coding error -- every tempid in a :db/add entity should resolve or be allocated.
|
||||||
(OpType::Retract, _, _) => bail!(ErrorKind::NotYetImplemented(format!("[:db/retract ...] entity referenced tempid that did not upsert: one of {}, {}", t1, t2))),
|
(OpType::Retract, _, _) => bail!(DbError::NotYetImplemented(format!("[:db/retract ...] entity referenced tempid that did not upsert: one of {}, {}", t1, t2))),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Term::AddOrRetract(op, Right(t), a, Left(v)) => {
|
Term::AddOrRetract(op, Right(t), a, Left(v)) => {
|
||||||
match (op, temp_id_map.get(&*t)) {
|
match (op, temp_id_map.get(&*t)) {
|
||||||
(op, Some(&n)) => Term::AddOrRetract(op, n, a, v),
|
(op, Some(&n)) => Term::AddOrRetract(op, n, a, v),
|
||||||
(OpType::Add, _) => unreachable!(), // This is a coding error.
|
(OpType::Add, _) => unreachable!(), // This is a coding error.
|
||||||
(OpType::Retract, _) => bail!(ErrorKind::NotYetImplemented(format!("[:db/retract ...] entity referenced tempid that did not upsert: {}", t))),
|
(OpType::Retract, _) => bail!(DbError::NotYetImplemented(format!("[:db/retract ...] entity referenced tempid that did not upsert: {}", t))),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Term::AddOrRetract(op, Left(e), a, Right(t)) => {
|
Term::AddOrRetract(op, Left(e), a, Right(t)) => {
|
||||||
match (op, temp_id_map.get(&*t)) {
|
match (op, temp_id_map.get(&*t)) {
|
||||||
(op, Some(&n)) => Term::AddOrRetract(op, e, a, TypedValue::Ref(n.0)),
|
(op, Some(&n)) => Term::AddOrRetract(op, e, a, TypedValue::Ref(n.0)),
|
||||||
(OpType::Add, _) => unreachable!(), // This is a coding error.
|
(OpType::Add, _) => unreachable!(), // This is a coding error.
|
||||||
(OpType::Retract, _) => bail!(ErrorKind::NotYetImplemented(format!("[:db/retract ...] entity referenced tempid that did not upsert: {}", t))),
|
(OpType::Retract, _) => bail!(DbError::NotYetImplemented(format!("[:db/retract ...] entity referenced tempid that did not upsert: {}", t))),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Term::AddOrRetract(_, Left(_), _, Left(_)) => unreachable!(), // This is a coding error -- these should not be in allocations.
|
Term::AddOrRetract(_, Left(_), _, Left(_)) => unreachable!(), // This is a coding error -- these should not be in allocations.
|
||||||
|
|
|
@ -67,9 +67,12 @@
|
||||||
//! propogation. These types have implemented [From](std::convert::From) such that conversion from the Rust type
|
//! propogation. These types have implemented [From](std::convert::From) such that conversion from the Rust type
|
||||||
//! to the C type is as painless as possible.
|
//! to the C type is as painless as possible.
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
extern crate mentat;
|
extern crate mentat;
|
||||||
|
|
||||||
|
use core::fmt::Display;
|
||||||
|
|
||||||
use std::collections::{
|
use std::collections::{
|
||||||
BTreeSet,
|
BTreeSet,
|
||||||
};
|
};
|
||||||
|
@ -204,7 +207,7 @@ pub struct ExternResult {
|
||||||
pub err: *const c_char,
|
pub err: *const c_char,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, E> From<Result<T, E>> for ExternResult where E: std::error::Error {
|
impl<T, E> From<Result<T, E>> for ExternResult where E: Display {
|
||||||
fn from(result: Result<T, E>) -> Self {
|
fn from(result: Result<T, E>) -> Self {
|
||||||
match result {
|
match result {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
|
|
|
@ -13,8 +13,7 @@ extern crate edn;
|
||||||
extern crate itertools;
|
extern crate itertools;
|
||||||
|
|
||||||
/// A `ValueParseError` is a `combine::primitives::ParseError`-alike that implements the `Debug`,
|
/// A `ValueParseError` is a `combine::primitives::ParseError`-alike that implements the `Debug`,
|
||||||
/// `Display`, and `std::error::Error` traits. In addition, it doesn't capture references, making
|
/// `Display`, and `std::error::Error` traits. In addition, it doesn't capture references.
|
||||||
/// it possible to store `ValueParseError` instances in local links with the `error-chain` crate.
|
|
||||||
///
|
///
|
||||||
/// This is achieved by wrapping slices of type `&'a [edn::Value]` in an owning type that implements
|
/// This is achieved by wrapping slices of type `&'a [edn::Value]` in an owning type that implements
|
||||||
/// `Display`; rather than introducing a newtype like `DisplayVec`, we re-use `edn::Value::Vector`.
|
/// `Display`; rather than introducing a newtype like `DisplayVec`, we re-use `edn::Value::Vector`.
|
||||||
|
|
|
@ -4,7 +4,8 @@ version = "0.0.1"
|
||||||
workspace = ".."
|
workspace = ".."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
|
failure = "0.1.1"
|
||||||
|
failure_derive = "0.1.1"
|
||||||
|
|
||||||
[dependencies.mentat_core]
|
[dependencies.mentat_core]
|
||||||
path = "../core"
|
path = "../core"
|
||||||
|
|
|
@ -28,7 +28,7 @@ use clauses::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
AlgebrizerError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -80,12 +80,12 @@ impl ValueTypes for FnArg {
|
||||||
|
|
||||||
&FnArg::Constant(NonIntegerConstant::BigInteger(_)) => {
|
&FnArg::Constant(NonIntegerConstant::BigInteger(_)) => {
|
||||||
// Not yet implemented.
|
// Not yet implemented.
|
||||||
bail!(ErrorKind::UnsupportedArgument)
|
bail!(AlgebrizerError::UnsupportedArgument)
|
||||||
},
|
},
|
||||||
|
|
||||||
// These don't make sense here. TODO: split FnArg into scalar and non-scalar…
|
// These don't make sense here. TODO: split FnArg into scalar and non-scalar…
|
||||||
&FnArg::Vector(_) |
|
&FnArg::Vector(_) |
|
||||||
&FnArg::SrcVar(_) => bail!(ErrorKind::UnsupportedArgument),
|
&FnArg::SrcVar(_) => bail!(AlgebrizerError::UnsupportedArgument),
|
||||||
|
|
||||||
// These are all straightforward.
|
// These are all straightforward.
|
||||||
&FnArg::Constant(NonIntegerConstant::Boolean(_)) => ValueTypeSet::of_one(ValueType::Boolean),
|
&FnArg::Constant(NonIntegerConstant::Boolean(_)) => ValueTypeSet::of_one(ValueType::Boolean),
|
||||||
|
@ -196,7 +196,7 @@ impl ConjoiningClauses {
|
||||||
FnArg::Variable(in_var) => {
|
FnArg::Variable(in_var) => {
|
||||||
// TODO: technically you could ground an existing variable inside the query….
|
// TODO: technically you could ground an existing variable inside the query….
|
||||||
if !self.input_variables.contains(&in_var) {
|
if !self.input_variables.contains(&in_var) {
|
||||||
bail!(ErrorKind::UnboundVariable((*in_var.0).clone()));
|
bail!(AlgebrizerError::UnboundVariable((*in_var.0).clone()))
|
||||||
}
|
}
|
||||||
match self.bound_value(&in_var) {
|
match self.bound_value(&in_var) {
|
||||||
// The type is already known if it's a bound variable….
|
// The type is already known if it's a bound variable….
|
||||||
|
@ -205,7 +205,7 @@ impl ConjoiningClauses {
|
||||||
// The variable is present in `:in`, but it hasn't yet been provided.
|
// 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
|
// 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.
|
// to collect variables as part of a computed table or substitution.
|
||||||
bail!(ErrorKind::UnboundVariable((*in_var.0).clone()))
|
bail!(AlgebrizerError::UnboundVariable((*in_var.0).clone()))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -215,7 +215,7 @@ impl ConjoiningClauses {
|
||||||
|
|
||||||
// These don't make sense here.
|
// These don't make sense here.
|
||||||
FnArg::Vector(_) |
|
FnArg::Vector(_) |
|
||||||
FnArg::SrcVar(_) => bail!(ErrorKind::InvalidGroundConstant),
|
FnArg::SrcVar(_) => bail!(AlgebrizerError::InvalidGroundConstant),
|
||||||
|
|
||||||
// These are all straightforward.
|
// These are all straightforward.
|
||||||
FnArg::Constant(NonIntegerConstant::Boolean(x)) => {
|
FnArg::Constant(NonIntegerConstant::Boolean(x)) => {
|
||||||
|
|
|
@ -30,8 +30,9 @@ use clauses::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
|
AlgebrizerError,
|
||||||
BindingError,
|
BindingError,
|
||||||
ErrorKind,
|
InvalidBinding,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,17 +54,17 @@ impl ConjoiningClauses {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
pub(crate) fn apply_fulltext(&mut self, known: Known, where_fn: WhereFn) -> Result<()> {
|
pub(crate) fn apply_fulltext(&mut self, known: Known, where_fn: WhereFn) -> Result<()> {
|
||||||
if where_fn.args.len() != 3 {
|
if where_fn.args.len() != 3 {
|
||||||
bail!(ErrorKind::InvalidNumberOfArguments(where_fn.operator.clone(), where_fn.args.len(), 3));
|
bail!(AlgebrizerError::InvalidNumberOfArguments(where_fn.operator.clone(), where_fn.args.len(), 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
if where_fn.binding.is_empty() {
|
if where_fn.binding.is_empty() {
|
||||||
// The binding must introduce at least one bound variable.
|
// The binding must introduce at least one bound variable.
|
||||||
bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(), BindingError::NoBoundVariable));
|
bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::NoBoundVariable));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !where_fn.binding.is_valid() {
|
if !where_fn.binding.is_valid() {
|
||||||
// The binding must not duplicate bound variables.
|
// The binding must not duplicate bound variables.
|
||||||
bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should have exactly four bindings. Destructure them now.
|
// We should have exactly four bindings. Destructure them now.
|
||||||
|
@ -71,17 +72,18 @@ impl ConjoiningClauses {
|
||||||
Binding::BindRel(bindings) => {
|
Binding::BindRel(bindings) => {
|
||||||
let bindings_count = bindings.len();
|
let bindings_count = bindings.len();
|
||||||
if bindings_count < 1 || bindings_count > 4 {
|
if bindings_count < 1 || bindings_count > 4 {
|
||||||
bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(),
|
bail!(InvalidBinding::new(where_fn.operator.clone(),
|
||||||
BindingError::InvalidNumberOfBindings {
|
BindingError::InvalidNumberOfBindings {
|
||||||
number: bindings.len(),
|
number: bindings.len(),
|
||||||
expected: 4,
|
expected: 4,
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
bindings
|
bindings
|
||||||
},
|
},
|
||||||
Binding::BindScalar(_) |
|
Binding::BindScalar(_) |
|
||||||
Binding::BindTuple(_) |
|
Binding::BindTuple(_) |
|
||||||
Binding::BindColl(_) => bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(), BindingError::ExpectedBindRel)),
|
Binding::BindColl(_) => bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::ExpectedBindRel)),
|
||||||
};
|
};
|
||||||
let mut bindings = bindings.into_iter();
|
let mut bindings = bindings.into_iter();
|
||||||
let b_entity = bindings.next().unwrap();
|
let b_entity = bindings.next().unwrap();
|
||||||
|
@ -94,7 +96,7 @@ impl ConjoiningClauses {
|
||||||
// TODO: process source variables.
|
// TODO: process source variables.
|
||||||
match args.next().unwrap() {
|
match args.next().unwrap() {
|
||||||
FnArg::SrcVar(SrcVar::DefaultSrc) => {},
|
FnArg::SrcVar(SrcVar::DefaultSrc) => {},
|
||||||
_ => bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "source variable", 0)),
|
_ => bail!(AlgebrizerError::InvalidArgument(where_fn.operator.clone(), "source variable", 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
let schema = known.schema;
|
let schema = known.schema;
|
||||||
|
@ -114,10 +116,10 @@ impl ConjoiningClauses {
|
||||||
match self.bound_value(&v) {
|
match self.bound_value(&v) {
|
||||||
Some(TypedValue::Ref(entid)) => Some(entid),
|
Some(TypedValue::Ref(entid)) => Some(entid),
|
||||||
Some(tv) => {
|
Some(tv) => {
|
||||||
bail!(ErrorKind::InputTypeDisagreement(v.name().clone(), ValueType::Ref, tv.value_type()));
|
bail!(AlgebrizerError::InputTypeDisagreement(v.name().clone(), ValueType::Ref, tv.value_type()))
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
bail!(ErrorKind::UnboundVariable((*v.0).clone()));
|
bail!(AlgebrizerError::UnboundVariable((*v.0).clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -127,10 +129,10 @@ impl ConjoiningClauses {
|
||||||
// An unknown ident, or an entity that isn't present in the store, or isn't a fulltext
|
// 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
|
// attribute, is likely enough to be a coding error that we choose to bail instead of
|
||||||
// marking the pattern as known-empty.
|
// marking the pattern as known-empty.
|
||||||
let a = a.ok_or(ErrorKind::InvalidArgument(where_fn.operator.clone(), "attribute", 1))?;
|
let a = a.ok_or(AlgebrizerError::InvalidArgument(where_fn.operator.clone(), "attribute", 1))?;
|
||||||
let attribute = schema.attribute_for_entid(a)
|
let attribute = schema.attribute_for_entid(a)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or(ErrorKind::InvalidArgument(where_fn.operator.clone(),
|
.ok_or(AlgebrizerError::InvalidArgument(where_fn.operator.clone(),
|
||||||
"attribute", 1))?;
|
"attribute", 1))?;
|
||||||
|
|
||||||
if !attribute.fulltext {
|
if !attribute.fulltext {
|
||||||
|
@ -169,18 +171,18 @@ impl ConjoiningClauses {
|
||||||
FnArg::Variable(in_var) => {
|
FnArg::Variable(in_var) => {
|
||||||
match self.bound_value(&in_var) {
|
match self.bound_value(&in_var) {
|
||||||
Some(t @ TypedValue::String(_)) => Either::Left(t),
|
Some(t @ TypedValue::String(_)) => Either::Left(t),
|
||||||
Some(_) => bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "string", 2)),
|
Some(_) => bail!(AlgebrizerError::InvalidArgument(where_fn.operator.clone(), "string", 2)),
|
||||||
None => {
|
None => {
|
||||||
// Regardless of whether we'll be providing a string later, or the value
|
// Regardless of whether we'll be providing a string later, or the value
|
||||||
// comes from a column, it must be a string.
|
// comes from a column, it must be a string.
|
||||||
if self.known_type(&in_var) != Some(ValueType::String) {
|
if self.known_type(&in_var) != Some(ValueType::String) {
|
||||||
bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "string", 2));
|
bail!(AlgebrizerError::InvalidArgument(where_fn.operator.clone(), "string", 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.input_variables.contains(&in_var) {
|
if self.input_variables.contains(&in_var) {
|
||||||
// Sorry, we haven't implemented late binding.
|
// Sorry, we haven't implemented late binding.
|
||||||
// TODO: implement this.
|
// TODO: implement this.
|
||||||
bail!(ErrorKind::UnboundVariable((*in_var.0).clone()));
|
bail!(AlgebrizerError::UnboundVariable((*in_var.0).clone()))
|
||||||
} else {
|
} else {
|
||||||
// It must be bound earlier in the query. We already established that
|
// It must be bound earlier in the query. We already established that
|
||||||
// it must be a string column.
|
// it must be a string column.
|
||||||
|
@ -189,13 +191,13 @@ impl ConjoiningClauses {
|
||||||
.and_then(|bindings| bindings.get(0).cloned()) {
|
.and_then(|bindings| bindings.get(0).cloned()) {
|
||||||
Either::Right(binding)
|
Either::Right(binding)
|
||||||
} else {
|
} else {
|
||||||
bail!(ErrorKind::UnboundVariable((*in_var.0).clone()));
|
bail!(AlgebrizerError::UnboundVariable((*in_var.0).clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "string", 2)),
|
_ => bail!(AlgebrizerError::InvalidArgument(where_fn.operator.clone(), "string", 2)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let qv = match search {
|
let qv = match search {
|
||||||
|
@ -244,7 +246,7 @@ impl ConjoiningClauses {
|
||||||
|
|
||||||
// We do not allow the score to be bound.
|
// We do not allow the score to be bound.
|
||||||
if self.value_bindings.contains_key(var) || self.input_variables.contains(var) {
|
if self.value_bindings.contains_key(var) || self.input_variables.contains(var) {
|
||||||
bail!(ErrorKind::InvalidBinding(var.name(), BindingError::UnexpectedBinding));
|
bail!(InvalidBinding::new(var.name(), BindingError::UnexpectedBinding));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We bind the value ourselves. This handily takes care of substituting into existing uses.
|
// We bind the value ourselves. This handily takes care of substituting into existing uses.
|
||||||
|
|
|
@ -31,8 +31,9 @@ use clauses::{
|
||||||
use clauses::convert::ValueConversion;
|
use clauses::convert::ValueConversion;
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
|
AlgebrizerError,
|
||||||
BindingError,
|
BindingError,
|
||||||
ErrorKind,
|
InvalidBinding,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -117,19 +118,19 @@ impl ConjoiningClauses {
|
||||||
|
|
||||||
pub(crate) fn apply_ground(&mut self, known: Known, where_fn: WhereFn) -> Result<()> {
|
pub(crate) fn apply_ground(&mut self, known: Known, where_fn: WhereFn) -> Result<()> {
|
||||||
if where_fn.args.len() != 1 {
|
if where_fn.args.len() != 1 {
|
||||||
bail!(ErrorKind::InvalidNumberOfArguments(where_fn.operator.clone(), where_fn.args.len(), 1));
|
bail!(AlgebrizerError::InvalidNumberOfArguments(where_fn.operator.clone(), where_fn.args.len(), 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut args = where_fn.args.into_iter();
|
let mut args = where_fn.args.into_iter();
|
||||||
|
|
||||||
if where_fn.binding.is_empty() {
|
if where_fn.binding.is_empty() {
|
||||||
// The binding must introduce at least one bound variable.
|
// The binding must introduce at least one bound variable.
|
||||||
bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(), BindingError::NoBoundVariable));
|
bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::NoBoundVariable));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !where_fn.binding.is_valid() {
|
if !where_fn.binding.is_valid() {
|
||||||
// The binding must not duplicate bound variables.
|
// The binding must not duplicate bound variables.
|
||||||
bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
||||||
}
|
}
|
||||||
|
|
||||||
let schema = known.schema;
|
let schema = known.schema;
|
||||||
|
@ -145,7 +146,7 @@ impl ConjoiningClauses {
|
||||||
// Just the same, but we bind more than one column at a time.
|
// Just the same, but we bind more than one column at a time.
|
||||||
if children.len() != places.len() {
|
if children.len() != places.len() {
|
||||||
// Number of arguments don't match the number of values. TODO: better error message.
|
// Number of arguments don't match the number of values. TODO: better error message.
|
||||||
bail!(ErrorKind::GroundBindingsMismatch);
|
bail!(AlgebrizerError::GroundBindingsMismatch)
|
||||||
}
|
}
|
||||||
for (place, arg) in places.into_iter().zip(children.into_iter()) {
|
for (place, arg) in places.into_iter().zip(children.into_iter()) {
|
||||||
self.apply_ground_place(schema, place, arg)? // TODO: short-circuit on impossible.
|
self.apply_ground_place(schema, place, arg)? // TODO: short-circuit on impossible.
|
||||||
|
@ -159,7 +160,7 @@ impl ConjoiningClauses {
|
||||||
// are all in a single structure. That makes it substantially simpler!
|
// are all in a single structure. That makes it substantially simpler!
|
||||||
(Binding::BindColl(var), FnArg::Vector(children)) => {
|
(Binding::BindColl(var), FnArg::Vector(children)) => {
|
||||||
if children.is_empty() {
|
if children.is_empty() {
|
||||||
bail!(ErrorKind::InvalidGroundConstant);
|
bail!(AlgebrizerError::InvalidGroundConstant)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn a collection of arguments into a Vec of `TypedValue`s of the same type.
|
// Turn a collection of arguments into a Vec of `TypedValue`s of the same type.
|
||||||
|
@ -177,7 +178,7 @@ impl ConjoiningClauses {
|
||||||
if accumulated_types.insert(tv.value_type()) &&
|
if accumulated_types.insert(tv.value_type()) &&
|
||||||
!accumulated_types.is_unit() {
|
!accumulated_types.is_unit() {
|
||||||
// Values not all of the same type.
|
// Values not all of the same type.
|
||||||
Some(Err(ErrorKind::InvalidGroundConstant.into()))
|
Some(Err(AlgebrizerError::InvalidGroundConstant.into()))
|
||||||
} else {
|
} else {
|
||||||
Some(Ok(tv))
|
Some(Ok(tv))
|
||||||
}
|
}
|
||||||
|
@ -208,7 +209,7 @@ impl ConjoiningClauses {
|
||||||
|
|
||||||
(Binding::BindRel(places), FnArg::Vector(rows)) => {
|
(Binding::BindRel(places), FnArg::Vector(rows)) => {
|
||||||
if rows.is_empty() {
|
if rows.is_empty() {
|
||||||
bail!(ErrorKind::InvalidGroundConstant);
|
bail!(AlgebrizerError::InvalidGroundConstant)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab the known types to which these args must conform, and track
|
// Grab the known types to which these args must conform, and track
|
||||||
|
@ -229,7 +230,7 @@ impl ConjoiningClauses {
|
||||||
|
|
||||||
if expected_width == 0 {
|
if expected_width == 0 {
|
||||||
// They can't all be placeholders.
|
// They can't all be placeholders.
|
||||||
bail!(ErrorKind::InvalidGroundConstant);
|
bail!(AlgebrizerError::InvalidGroundConstant)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accumulate values into `matrix` and types into `a_t_f_c`.
|
// Accumulate values into `matrix` and types into `a_t_f_c`.
|
||||||
|
@ -245,7 +246,7 @@ impl ConjoiningClauses {
|
||||||
FnArg::Vector(cols) => {
|
FnArg::Vector(cols) => {
|
||||||
// Make sure that every row is the same length.
|
// Make sure that every row is the same length.
|
||||||
if cols.len() != full_width {
|
if cols.len() != full_width {
|
||||||
bail!(ErrorKind::InvalidGroundConstant);
|
bail!(AlgebrizerError::InvalidGroundConstant)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: don't accumulate twice.
|
// TODO: don't accumulate twice.
|
||||||
|
@ -280,13 +281,13 @@ impl ConjoiningClauses {
|
||||||
let inserted = acc.insert(val.value_type());
|
let inserted = acc.insert(val.value_type());
|
||||||
if inserted && !acc.is_unit() {
|
if inserted && !acc.is_unit() {
|
||||||
// Heterogeneous types.
|
// Heterogeneous types.
|
||||||
bail!(ErrorKind::InvalidGroundConstant);
|
bail!(AlgebrizerError::InvalidGroundConstant)
|
||||||
}
|
}
|
||||||
matrix.push(val);
|
matrix.push(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
_ => bail!(ErrorKind::InvalidGroundConstant),
|
_ => bail!(AlgebrizerError::InvalidGroundConstant),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +313,7 @@ impl ConjoiningClauses {
|
||||||
self.collect_named_bindings(schema, names, types, matrix);
|
self.collect_named_bindings(schema, names, types, matrix);
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
(_, _) => bail!(ErrorKind::InvalidGroundConstant),
|
(_, _) => bail!(AlgebrizerError::InvalidGroundConstant),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use mentat_query::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
AlgebrizerError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ impl QueryInputs {
|
||||||
let old = types.insert(var.clone(), t);
|
let old = types.insert(var.clone(), t);
|
||||||
if let Some(old) = old {
|
if let Some(old) = old {
|
||||||
if old != t {
|
if old != t {
|
||||||
bail!(ErrorKind::InputTypeDisagreement(var.name(), old, t));
|
bail!(AlgebrizerError::InputTypeDisagreement(var.name(), old, t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,8 +53,7 @@ use mentat_query::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
Error,
|
AlgebrizerError,
|
||||||
ErrorKind,
|
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1014,7 +1013,7 @@ impl ConjoiningClauses {
|
||||||
|
|
||||||
let qa = self.extracted_types
|
let qa = self.extracted_types
|
||||||
.get(&var)
|
.get(&var)
|
||||||
.ok_or_else(|| Error::from_kind(ErrorKind::UnboundVariable(var.name())))?;
|
.ok_or_else(|| AlgebrizerError::UnboundVariable(var.name()))?;
|
||||||
self.wheres.add_intersection(ColumnConstraint::HasTypes {
|
self.wheres.add_intersection(ColumnConstraint::HasTypes {
|
||||||
value: qa.0.clone(),
|
value: qa.0.clone(),
|
||||||
value_types: types,
|
value_types: types,
|
||||||
|
|
|
@ -17,7 +17,7 @@ use mentat_query::{
|
||||||
use clauses::ConjoiningClauses;
|
use clauses::ConjoiningClauses;
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
AlgebrizerError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ impl ConjoiningClauses {
|
||||||
let col = self.column_bindings.get(&v).unwrap()[0].clone();
|
let col = self.column_bindings.get(&v).unwrap()[0].clone();
|
||||||
template.column_bindings.insert(v.clone(), vec![col]);
|
template.column_bindings.insert(v.clone(), vec![col]);
|
||||||
} else {
|
} else {
|
||||||
bail!(ErrorKind::UnboundVariable(v.name()));
|
bail!(AlgebrizerError::UnboundVariable(v.name()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,8 +111,7 @@ mod testing {
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
Error,
|
AlgebrizerError,
|
||||||
ErrorKind,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use types::{
|
use types::{
|
||||||
|
@ -553,10 +552,9 @@ mod testing {
|
||||||
:in ?y
|
:in ?y
|
||||||
:where (not [?x :foo/knows ?y])]"#;
|
:where (not [?x :foo/knows ?y])]"#;
|
||||||
let parsed = parse_find_string(query).expect("parse failed");
|
let parsed = parse_find_string(query).expect("parse failed");
|
||||||
let err = algebrize(known, parsed).err();
|
let err = algebrize(known, parsed).expect_err("algebrization should have failed");
|
||||||
assert!(err.is_some());
|
match err.downcast().expect("expected AlgebrizerError") {
|
||||||
match err.unwrap() {
|
AlgebrizerError::UnboundVariable(var) => { assert_eq!(var, PlainSymbol("?x".to_string())); },
|
||||||
Error(ErrorKind::UnboundVariable(var), _) => { assert_eq!(var, PlainSymbol("?x".to_string())); },
|
|
||||||
x => panic!("expected Unbound Variable error, got {:?}", x),
|
x => panic!("expected Unbound Variable error, got {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@ use clauses::ConjoiningClauses;
|
||||||
use clauses::convert::ValueTypes;
|
use clauses::convert::ValueTypes;
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
|
AlgebrizerError,
|
||||||
Result,
|
Result,
|
||||||
ErrorKind,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use types::{
|
use types::{
|
||||||
|
@ -53,7 +53,7 @@ impl ConjoiningClauses {
|
||||||
if let Some(op) = Inequality::from_datalog_operator(predicate.operator.0.as_str()) {
|
if let Some(op) = Inequality::from_datalog_operator(predicate.operator.0.as_str()) {
|
||||||
self.apply_inequality(known, op, predicate)
|
self.apply_inequality(known, op, predicate)
|
||||||
} else {
|
} else {
|
||||||
bail!(ErrorKind::UnknownFunction(predicate.operator.clone()))
|
bail!(AlgebrizerError::UnknownFunction(predicate.operator.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ impl ConjoiningClauses {
|
||||||
pub(crate) fn apply_type_anno(&mut self, anno: &TypeAnnotation) -> Result<()> {
|
pub(crate) fn apply_type_anno(&mut self, anno: &TypeAnnotation) -> Result<()> {
|
||||||
match ValueType::from_keyword(&anno.value_type) {
|
match ValueType::from_keyword(&anno.value_type) {
|
||||||
Some(value_type) => self.add_type_requirement(anno.variable.clone(), ValueTypeSet::of_one(value_type)),
|
Some(value_type) => self.add_type_requirement(anno.variable.clone(), ValueTypeSet::of_one(value_type)),
|
||||||
None => bail!(ErrorKind::InvalidArgumentType(PlainSymbol::plain("type"), ValueTypeSet::any(), 2)),
|
None => bail!(AlgebrizerError::InvalidArgumentType(PlainSymbol::plain("type"), ValueTypeSet::any(), 2)),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ impl ConjoiningClauses {
|
||||||
/// - Accumulates an `Inequality` constraint into the `wheres` list.
|
/// - Accumulates an `Inequality` constraint into the `wheres` list.
|
||||||
pub(crate) fn apply_inequality(&mut self, known: Known, comparison: Inequality, predicate: Predicate) -> Result<()> {
|
pub(crate) fn apply_inequality(&mut self, known: Known, comparison: Inequality, predicate: Predicate) -> Result<()> {
|
||||||
if predicate.args.len() != 2 {
|
if predicate.args.len() != 2 {
|
||||||
bail!(ErrorKind::InvalidNumberOfArguments(predicate.operator.clone(), predicate.args.len(), 2));
|
bail!(AlgebrizerError::InvalidNumberOfArguments(predicate.operator.clone(), predicate.args.len(), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go from arguments -- parser output -- to columns or values.
|
// Go from arguments -- parser output -- to columns or values.
|
||||||
|
@ -97,13 +97,13 @@ impl ConjoiningClauses {
|
||||||
let mut left_types = self.potential_types(known.schema, &left)?
|
let mut left_types = self.potential_types(known.schema, &left)?
|
||||||
.intersection(&supported_types);
|
.intersection(&supported_types);
|
||||||
if left_types.is_empty() {
|
if left_types.is_empty() {
|
||||||
bail!(ErrorKind::InvalidArgumentType(predicate.operator.clone(), supported_types, 0));
|
bail!(AlgebrizerError::InvalidArgumentType(predicate.operator.clone(), supported_types, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut right_types = self.potential_types(known.schema, &right)?
|
let mut right_types = self.potential_types(known.schema, &right)?
|
||||||
.intersection(&supported_types);
|
.intersection(&supported_types);
|
||||||
if right_types.is_empty() {
|
if right_types.is_empty() {
|
||||||
bail!(ErrorKind::InvalidArgumentType(predicate.operator.clone(), supported_types, 1));
|
bail!(AlgebrizerError::InvalidArgumentType(predicate.operator.clone(), supported_types, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We would like to allow longs to compare to doubles.
|
// We would like to allow longs to compare to doubles.
|
||||||
|
@ -150,7 +150,7 @@ impl ConjoiningClauses {
|
||||||
left_v = self.resolve_ref_argument(known.schema, &predicate.operator, 0, left)?;
|
left_v = self.resolve_ref_argument(known.schema, &predicate.operator, 0, left)?;
|
||||||
right_v = self.resolve_ref_argument(known.schema, &predicate.operator, 1, right)?;
|
right_v = self.resolve_ref_argument(known.schema, &predicate.operator, 1, right)?;
|
||||||
} else {
|
} else {
|
||||||
bail!(ErrorKind::InvalidArgumentType(predicate.operator.clone(), supported_types, 0));
|
bail!(AlgebrizerError::InvalidArgumentType(predicate.operator.clone(), supported_types, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// These arguments must be variables or instant/numeric constants.
|
// These arguments must be variables or instant/numeric constants.
|
||||||
|
|
|
@ -24,9 +24,8 @@ use mentat_query::{
|
||||||
use clauses::ConjoiningClauses;
|
use clauses::ConjoiningClauses;
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
|
AlgebrizerError,
|
||||||
Result,
|
Result,
|
||||||
Error,
|
|
||||||
ErrorKind,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use types::{
|
use types::{
|
||||||
|
@ -50,14 +49,14 @@ impl ConjoiningClauses {
|
||||||
if v.value_type().is_numeric() {
|
if v.value_type().is_numeric() {
|
||||||
Ok(QueryValue::TypedValue(v))
|
Ok(QueryValue::TypedValue(v))
|
||||||
} else {
|
} else {
|
||||||
bail!(ErrorKind::InputTypeDisagreement(var.name().clone(), ValueType::Long, v.value_type()));
|
bail!(AlgebrizerError::InputTypeDisagreement(var.name().clone(), ValueType::Long, v.value_type()))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.constrain_var_to_numeric(var.clone());
|
self.constrain_var_to_numeric(var.clone());
|
||||||
self.column_bindings
|
self.column_bindings
|
||||||
.get(&var)
|
.get(&var)
|
||||||
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
|
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
|
||||||
.ok_or_else(|| Error::from_kind(ErrorKind::UnboundVariable(var.name())))
|
.ok_or_else(|| AlgebrizerError::UnboundVariable(var.name()).into())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Can't be an entid.
|
// Can't be an entid.
|
||||||
|
@ -71,7 +70,7 @@ impl ConjoiningClauses {
|
||||||
Constant(NonIntegerConstant::BigInteger(_)) |
|
Constant(NonIntegerConstant::BigInteger(_)) |
|
||||||
Vector(_) => {
|
Vector(_) => {
|
||||||
self.mark_known_empty(EmptyBecause::NonNumericArgument);
|
self.mark_known_empty(EmptyBecause::NonNumericArgument);
|
||||||
bail!(ErrorKind::InvalidArgument(function.clone(), "numeric", position));
|
bail!(AlgebrizerError::InvalidArgument(function.clone(), "numeric", position))
|
||||||
},
|
},
|
||||||
Constant(NonIntegerConstant::Float(f)) => Ok(QueryValue::TypedValue(TypedValue::Double(f))),
|
Constant(NonIntegerConstant::Float(f)) => Ok(QueryValue::TypedValue(TypedValue::Double(f))),
|
||||||
}
|
}
|
||||||
|
@ -84,13 +83,13 @@ impl ConjoiningClauses {
|
||||||
FnArg::Variable(var) => {
|
FnArg::Variable(var) => {
|
||||||
match self.bound_value(&var) {
|
match self.bound_value(&var) {
|
||||||
Some(TypedValue::Instant(v)) => Ok(QueryValue::TypedValue(TypedValue::Instant(v))),
|
Some(TypedValue::Instant(v)) => Ok(QueryValue::TypedValue(TypedValue::Instant(v))),
|
||||||
Some(v) => bail!(ErrorKind::InputTypeDisagreement(var.name().clone(), ValueType::Instant, v.value_type())),
|
Some(v) => bail!(AlgebrizerError::InputTypeDisagreement(var.name().clone(), ValueType::Instant, v.value_type())),
|
||||||
None => {
|
None => {
|
||||||
self.constrain_var_to_type(var.clone(), ValueType::Instant);
|
self.constrain_var_to_type(var.clone(), ValueType::Instant);
|
||||||
self.column_bindings
|
self.column_bindings
|
||||||
.get(&var)
|
.get(&var)
|
||||||
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
|
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
|
||||||
.ok_or_else(|| Error::from_kind(ErrorKind::UnboundVariable(var.name())))
|
.ok_or_else(|| AlgebrizerError::UnboundVariable(var.name()).into())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -109,7 +108,7 @@ impl ConjoiningClauses {
|
||||||
Constant(NonIntegerConstant::BigInteger(_)) |
|
Constant(NonIntegerConstant::BigInteger(_)) |
|
||||||
Vector(_) => {
|
Vector(_) => {
|
||||||
self.mark_known_empty(EmptyBecause::NonInstantArgument);
|
self.mark_known_empty(EmptyBecause::NonInstantArgument);
|
||||||
bail!(ErrorKind::InvalidArgumentType(function.clone(), ValueType::Instant.into(), position));
|
bail!(AlgebrizerError::InvalidArgumentType(function.clone(), ValueType::Instant.into(), position))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,14 +127,14 @@ impl ConjoiningClauses {
|
||||||
self.column_bindings
|
self.column_bindings
|
||||||
.get(&var)
|
.get(&var)
|
||||||
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
|
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
|
||||||
.ok_or_else(|| Error::from_kind(ErrorKind::UnboundVariable(var.name())))
|
.ok_or_else(|| AlgebrizerError::UnboundVariable(var.name()).into())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
EntidOrInteger(i) => Ok(QueryValue::TypedValue(TypedValue::Ref(i))),
|
EntidOrInteger(i) => Ok(QueryValue::TypedValue(TypedValue::Ref(i))),
|
||||||
IdentOrKeyword(i) => {
|
IdentOrKeyword(i) => {
|
||||||
schema.get_entid(&i)
|
schema.get_entid(&i)
|
||||||
.map(|known_entid| QueryValue::Entid(known_entid.into()))
|
.map(|known_entid| QueryValue::Entid(known_entid.into()))
|
||||||
.ok_or_else(|| Error::from_kind(ErrorKind::UnrecognizedIdent(i.to_string())))
|
.ok_or_else(|| AlgebrizerError::UnrecognizedIdent(i.to_string()).into())
|
||||||
},
|
},
|
||||||
Constant(NonIntegerConstant::Boolean(_)) |
|
Constant(NonIntegerConstant::Boolean(_)) |
|
||||||
Constant(NonIntegerConstant::Float(_)) |
|
Constant(NonIntegerConstant::Float(_)) |
|
||||||
|
@ -146,7 +145,7 @@ impl ConjoiningClauses {
|
||||||
SrcVar(_) |
|
SrcVar(_) |
|
||||||
Vector(_) => {
|
Vector(_) => {
|
||||||
self.mark_known_empty(EmptyBecause::NonEntityArgument);
|
self.mark_known_empty(EmptyBecause::NonEntityArgument);
|
||||||
bail!(ErrorKind::InvalidArgumentType(function.clone(), ValueType::Ref.into(), position));
|
bail!(AlgebrizerError::InvalidArgumentType(function.clone(), ValueType::Ref.into(), position))
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -173,7 +172,7 @@ impl ConjoiningClauses {
|
||||||
self.column_bindings
|
self.column_bindings
|
||||||
.get(&var)
|
.get(&var)
|
||||||
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
|
.and_then(|cols| cols.first().map(|col| QueryValue::Column(col.clone())))
|
||||||
.ok_or_else(|| Error::from_kind(ErrorKind::UnboundVariable(var.name())))
|
.ok_or_else(|| AlgebrizerError::UnboundVariable(var.name()).into())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -25,8 +25,9 @@ use clauses::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
|
AlgebrizerError,
|
||||||
BindingError,
|
BindingError,
|
||||||
ErrorKind,
|
InvalidBinding,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,17 +61,17 @@ impl ConjoiningClauses {
|
||||||
// transactions that impact one of the given attributes.
|
// transactions that impact one of the given attributes.
|
||||||
pub(crate) fn apply_tx_ids(&mut self, known: Known, where_fn: WhereFn) -> Result<()> {
|
pub(crate) fn apply_tx_ids(&mut self, known: Known, where_fn: WhereFn) -> Result<()> {
|
||||||
if where_fn.args.len() != 3 {
|
if where_fn.args.len() != 3 {
|
||||||
bail!(ErrorKind::InvalidNumberOfArguments(where_fn.operator.clone(), where_fn.args.len(), 3));
|
bail!(AlgebrizerError::InvalidNumberOfArguments(where_fn.operator.clone(), where_fn.args.len(), 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
if where_fn.binding.is_empty() {
|
if where_fn.binding.is_empty() {
|
||||||
// The binding must introduce at least one bound variable.
|
// The binding must introduce at least one bound variable.
|
||||||
bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(), BindingError::NoBoundVariable));
|
bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::NoBoundVariable));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !where_fn.binding.is_valid() {
|
if !where_fn.binding.is_valid() {
|
||||||
// The binding must not duplicate bound variables.
|
// The binding must not duplicate bound variables.
|
||||||
bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should have exactly one binding. Destructure it now.
|
// We should have exactly one binding. Destructure it now.
|
||||||
|
@ -78,7 +79,7 @@ impl ConjoiningClauses {
|
||||||
Binding::BindRel(bindings) => {
|
Binding::BindRel(bindings) => {
|
||||||
let bindings_count = bindings.len();
|
let bindings_count = bindings.len();
|
||||||
if bindings_count != 1 {
|
if bindings_count != 1 {
|
||||||
bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(),
|
bail!(InvalidBinding::new(where_fn.operator.clone(),
|
||||||
BindingError::InvalidNumberOfBindings {
|
BindingError::InvalidNumberOfBindings {
|
||||||
number: bindings_count,
|
number: bindings_count,
|
||||||
expected: 1,
|
expected: 1,
|
||||||
|
@ -92,7 +93,7 @@ impl ConjoiningClauses {
|
||||||
Binding::BindColl(v) => v,
|
Binding::BindColl(v) => v,
|
||||||
Binding::BindScalar(_) |
|
Binding::BindScalar(_) |
|
||||||
Binding::BindTuple(_) => {
|
Binding::BindTuple(_) => {
|
||||||
bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(), BindingError::ExpectedBindRelOrBindColl))
|
bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::ExpectedBindRelOrBindColl))
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,7 +102,7 @@ impl ConjoiningClauses {
|
||||||
// TODO: process source variables.
|
// TODO: process source variables.
|
||||||
match args.next().unwrap() {
|
match args.next().unwrap() {
|
||||||
FnArg::SrcVar(SrcVar::DefaultSrc) => {},
|
FnArg::SrcVar(SrcVar::DefaultSrc) => {},
|
||||||
_ => bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "source variable", 0)),
|
_ => bail!(AlgebrizerError::InvalidArgument(where_fn.operator.clone(), "source variable", 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx1 = self.resolve_tx_argument(&known.schema, &where_fn.operator, 1, args.next().unwrap())?;
|
let tx1 = self.resolve_tx_argument(&known.schema, &where_fn.operator, 1, args.next().unwrap())?;
|
||||||
|
@ -138,17 +139,17 @@ impl ConjoiningClauses {
|
||||||
|
|
||||||
pub(crate) fn apply_tx_data(&mut self, known: Known, where_fn: WhereFn) -> Result<()> {
|
pub(crate) fn apply_tx_data(&mut self, known: Known, where_fn: WhereFn) -> Result<()> {
|
||||||
if where_fn.args.len() != 2 {
|
if where_fn.args.len() != 2 {
|
||||||
bail!(ErrorKind::InvalidNumberOfArguments(where_fn.operator.clone(), where_fn.args.len(), 2));
|
bail!(AlgebrizerError::InvalidNumberOfArguments(where_fn.operator.clone(), where_fn.args.len(), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
if where_fn.binding.is_empty() {
|
if where_fn.binding.is_empty() {
|
||||||
// The binding must introduce at least one bound variable.
|
// The binding must introduce at least one bound variable.
|
||||||
bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(), BindingError::NoBoundVariable));
|
bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::NoBoundVariable));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !where_fn.binding.is_valid() {
|
if !where_fn.binding.is_valid() {
|
||||||
// The binding must not duplicate bound variables.
|
// The binding must not duplicate bound variables.
|
||||||
bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should have at most five bindings. Destructure them now.
|
// We should have at most five bindings. Destructure them now.
|
||||||
|
@ -156,7 +157,7 @@ impl ConjoiningClauses {
|
||||||
Binding::BindRel(bindings) => {
|
Binding::BindRel(bindings) => {
|
||||||
let bindings_count = bindings.len();
|
let bindings_count = bindings.len();
|
||||||
if bindings_count < 1 || bindings_count > 5 {
|
if bindings_count < 1 || bindings_count > 5 {
|
||||||
bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(),
|
bail!(InvalidBinding::new(where_fn.operator.clone(),
|
||||||
BindingError::InvalidNumberOfBindings {
|
BindingError::InvalidNumberOfBindings {
|
||||||
number: bindings.len(),
|
number: bindings.len(),
|
||||||
expected: 5,
|
expected: 5,
|
||||||
|
@ -166,7 +167,7 @@ impl ConjoiningClauses {
|
||||||
},
|
},
|
||||||
Binding::BindScalar(_) |
|
Binding::BindScalar(_) |
|
||||||
Binding::BindTuple(_) |
|
Binding::BindTuple(_) |
|
||||||
Binding::BindColl(_) => bail!(ErrorKind::InvalidBinding(where_fn.operator.clone(), BindingError::ExpectedBindRel)),
|
Binding::BindColl(_) => bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::ExpectedBindRel)),
|
||||||
};
|
};
|
||||||
let mut bindings = bindings.into_iter();
|
let mut bindings = bindings.into_iter();
|
||||||
let b_e = bindings.next().unwrap_or(VariableOrPlaceholder::Placeholder);
|
let b_e = bindings.next().unwrap_or(VariableOrPlaceholder::Placeholder);
|
||||||
|
@ -180,7 +181,7 @@ impl ConjoiningClauses {
|
||||||
// TODO: process source variables.
|
// TODO: process source variables.
|
||||||
match args.next().unwrap() {
|
match args.next().unwrap() {
|
||||||
FnArg::SrcVar(SrcVar::DefaultSrc) => {},
|
FnArg::SrcVar(SrcVar::DefaultSrc) => {},
|
||||||
_ => bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "source variable", 0)),
|
_ => bail!(AlgebrizerError::InvalidArgument(where_fn.operator.clone(), "source variable", 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx = self.resolve_tx_argument(&known.schema, &where_fn.operator, 1, args.next().unwrap())?;
|
let tx = self.resolve_tx_argument(&known.schema, &where_fn.operator, 1, args.next().unwrap())?;
|
||||||
|
|
|
@ -17,7 +17,7 @@ use clauses::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
AlgebrizerError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ impl ConjoiningClauses {
|
||||||
"ground" => self.apply_ground(known, where_fn),
|
"ground" => self.apply_ground(known, where_fn),
|
||||||
"tx-data" => self.apply_tx_data(known, where_fn),
|
"tx-data" => self.apply_tx_data(known, where_fn),
|
||||||
"tx-ids" => self.apply_tx_ids(known, where_fn),
|
"tx-ids" => self.apply_tx_ids(known, where_fn),
|
||||||
_ => bail!(ErrorKind::UnknownFunction(where_fn.operator.clone())),
|
_ => bail!(AlgebrizerError::UnknownFunction(where_fn.operator.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,18 @@
|
||||||
|
|
||||||
extern crate mentat_query;
|
extern crate mentat_query;
|
||||||
|
|
||||||
|
use std; // To refer to std::result::Result.
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use failure::{
|
||||||
|
Backtrace,
|
||||||
|
Context,
|
||||||
|
Error,
|
||||||
|
Fail,
|
||||||
|
};
|
||||||
|
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
EdnParseError,
|
|
||||||
ValueType,
|
ValueType,
|
||||||
ValueTypeSet,
|
ValueTypeSet,
|
||||||
};
|
};
|
||||||
|
@ -20,7 +30,53 @@ use self::mentat_query::{
|
||||||
PlainSymbol,
|
PlainSymbol,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bail {
|
||||||
|
($e:expr) => (
|
||||||
|
return Err($e.into());
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InvalidBinding {
|
||||||
|
pub function: PlainSymbol,
|
||||||
|
pub inner: Context<BindingError>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InvalidBinding {
|
||||||
|
pub fn new(function: PlainSymbol, inner: BindingError) -> InvalidBinding {
|
||||||
|
InvalidBinding {
|
||||||
|
function: function,
|
||||||
|
inner: Context::new(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fail for InvalidBinding {
|
||||||
|
fn cause(&self) -> Option<&Fail> {
|
||||||
|
self.inner.cause()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backtrace(&self) -> Option<&Backtrace> {
|
||||||
|
self.inner.backtrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for InvalidBinding {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "invalid binding for {}: {:?}", self.function, self.inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for BindingError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "BindingError: {:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Fail)]
|
||||||
pub enum BindingError {
|
pub enum BindingError {
|
||||||
NoBoundVariable,
|
NoBoundVariable,
|
||||||
UnexpectedBinding,
|
UnexpectedBinding,
|
||||||
|
@ -40,98 +96,52 @@ pub enum BindingError {
|
||||||
InvalidNumberOfBindings { number: usize, expected: usize },
|
InvalidNumberOfBindings { number: usize, expected: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
error_chain! {
|
#[derive(Debug, Fail)]
|
||||||
types {
|
pub enum AlgebrizerError {
|
||||||
Error, ErrorKind, ResultExt, Result;
|
#[fail(display = "{} var {} is duplicated", _0, _1)]
|
||||||
}
|
DuplicateVariableError(PlainSymbol, &'static str),
|
||||||
|
|
||||||
foreign_links {
|
#[fail(display = "unexpected FnArg")]
|
||||||
EdnParseError(EdnParseError);
|
UnsupportedArgument,
|
||||||
}
|
|
||||||
|
|
||||||
errors {
|
#[fail(display = "value of type {} provided for var {}, expected {}", _0, _1, _2)]
|
||||||
UnsupportedArgument {
|
InputTypeDisagreement(PlainSymbol, ValueType, ValueType),
|
||||||
description("unexpected FnArg")
|
|
||||||
display("unexpected FnArg")
|
|
||||||
}
|
|
||||||
|
|
||||||
InputTypeDisagreement(var: PlainSymbol, declared: ValueType, provided: ValueType) {
|
#[fail(display = "invalid number of arguments to {}: expected {}, got {}.", _0, _1, _2)]
|
||||||
description("input type disagreement")
|
InvalidNumberOfArguments(PlainSymbol, usize, usize),
|
||||||
display("value of type {} provided for var {}, expected {}", provided, var, declared)
|
|
||||||
}
|
|
||||||
|
|
||||||
UnrecognizedIdent(ident: String) {
|
#[fail(display = "invalid argument to {}: expected {} in position {}.", _0, _1, _2)]
|
||||||
description("no entid found for ident")
|
InvalidArgument(PlainSymbol, &'static str, usize),
|
||||||
display("no entid found for ident: {}", ident)
|
|
||||||
}
|
|
||||||
|
|
||||||
UnknownFunction(name: PlainSymbol) {
|
#[fail(display = "invalid argument to {}: expected one of {:?} in position {}.", _0, _1, _2)]
|
||||||
description("no such function")
|
InvalidArgumentType(PlainSymbol, ValueTypeSet, usize),
|
||||||
display("no function named {}", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
InvalidNumberOfArguments(function: PlainSymbol, number: usize, expected: usize) {
|
|
||||||
description("invalid number of arguments")
|
|
||||||
display("invalid number of arguments to {}: expected {}, got {}.", function, expected, number)
|
|
||||||
}
|
|
||||||
|
|
||||||
UnboundVariable(name: PlainSymbol) {
|
|
||||||
description("unbound variable in order clause or function call")
|
|
||||||
display("unbound variable: {}", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
InvalidBinding(function: PlainSymbol, binding_error: BindingError) {
|
|
||||||
description("invalid binding")
|
|
||||||
display("invalid binding for {}: {:?}.", function, binding_error)
|
|
||||||
}
|
|
||||||
|
|
||||||
GroundBindingsMismatch {
|
|
||||||
description("mismatched bindings in ground")
|
|
||||||
display("mismatched bindings in ground")
|
|
||||||
}
|
|
||||||
|
|
||||||
InvalidGroundConstant {
|
|
||||||
// TODO: flesh this out.
|
// TODO: flesh this out.
|
||||||
description("invalid expression in ground constant")
|
#[fail(display = "invalid expression in ground constant")]
|
||||||
display("invalid expression in ground constant")
|
InvalidGroundConstant,
|
||||||
}
|
|
||||||
|
|
||||||
InvalidArgument(function: PlainSymbol, expected: &'static str, position: usize) {
|
#[fail(display = "invalid limit {} of type {}: expected natural number.", _0, _1)]
|
||||||
description("invalid argument")
|
InvalidLimit(String, ValueType),
|
||||||
display("invalid argument to {}: expected {} in position {}.", function, expected, position)
|
|
||||||
}
|
|
||||||
|
|
||||||
InvalidArgumentType(function: PlainSymbol, expected_types: ValueTypeSet, position: usize) {
|
#[fail(display = "mismatched bindings in ground")]
|
||||||
description("invalid argument")
|
GroundBindingsMismatch,
|
||||||
display("invalid argument to {}: expected one of {:?} in position {}.", function, expected_types, position)
|
|
||||||
}
|
|
||||||
|
|
||||||
InvalidLimit(val: String, kind: ValueType) {
|
#[fail(display = "no entid found for ident: {}", _0)]
|
||||||
description("invalid limit")
|
UnrecognizedIdent(String),
|
||||||
display("invalid limit {} of type {}: expected natural number.", val, kind)
|
|
||||||
}
|
#[fail(display = "no function named {}", _0)]
|
||||||
|
UnknownFunction(PlainSymbol),
|
||||||
|
|
||||||
|
#[fail(display = ":limit var {} not present in :in", _0)]
|
||||||
|
UnknownLimitVar(PlainSymbol),
|
||||||
|
|
||||||
|
#[fail(display = "unbound variable {} in order clause or function call", _0)]
|
||||||
|
UnboundVariable(PlainSymbol),
|
||||||
|
|
||||||
NonMatchingVariablesInOrClause {
|
|
||||||
// TODO: flesh out.
|
// TODO: flesh out.
|
||||||
description("non-matching variables in 'or' clause")
|
#[fail(display = "non-matching variables in 'or' clause")]
|
||||||
display("non-matching variables in 'or' clause")
|
NonMatchingVariablesInOrClause,
|
||||||
}
|
|
||||||
|
|
||||||
NonMatchingVariablesInNotClause {
|
#[fail(display = "non-matching variables in 'not' clause")]
|
||||||
// TODO: flesh out.
|
NonMatchingVariablesInNotClause,
|
||||||
description("non-matching variables in 'not' clause")
|
|
||||||
display("non-matching variables in 'not' clause")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DuplicateVariableError(name: PlainSymbol, clause: &'static str) {
|
|
||||||
description("duplicate variables")
|
|
||||||
display("{} var {} is duplicated", clause, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
UnknownLimitVar(name: PlainSymbol) {
|
|
||||||
description(":limit var not present in :in")
|
|
||||||
display(":limit var {} not present in :in", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,9 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
#![recursion_limit="128"]
|
extern crate failure;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use] extern crate failure_derive;
|
||||||
extern crate error_chain;
|
|
||||||
|
|
||||||
extern crate mentat_core;
|
extern crate mentat_core;
|
||||||
extern crate mentat_query;
|
extern crate mentat_query;
|
||||||
|
@ -20,6 +19,7 @@ use std::collections::BTreeSet;
|
||||||
use std::ops::Sub;
|
use std::ops::Sub;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
mod errors;
|
mod errors;
|
||||||
mod types;
|
mod types;
|
||||||
mod validate;
|
mod validate;
|
||||||
|
@ -48,10 +48,10 @@ use mentat_query::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use errors::{
|
pub use errors::{
|
||||||
|
AlgebrizerError,
|
||||||
BindingError,
|
BindingError,
|
||||||
Error,
|
|
||||||
ErrorKind,
|
|
||||||
Result,
|
Result,
|
||||||
|
InvalidBinding,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use clauses::{
|
pub use clauses::{
|
||||||
|
@ -216,7 +216,7 @@ fn validate_and_simplify_order(cc: &ConjoiningClauses, order: Option<Vec<Order>>
|
||||||
|
|
||||||
// Fail if the var isn't bound by the query.
|
// Fail if the var isn't bound by the query.
|
||||||
if !cc.column_bindings.contains_key(&var) {
|
if !cc.column_bindings.contains_key(&var) {
|
||||||
bail!(ErrorKind::UnboundVariable(var.name()));
|
bail!(AlgebrizerError::UnboundVariable(var.name()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, determine if we also need to order by type…
|
// Otherwise, determine if we also need to order by type…
|
||||||
|
@ -242,14 +242,14 @@ fn simplify_limit(mut query: AlgebraicQuery) -> Result<AlgebraicQuery> {
|
||||||
Some(TypedValue::Long(n)) => {
|
Some(TypedValue::Long(n)) => {
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
// User-specified limits should always be natural numbers (> 0).
|
// User-specified limits should always be natural numbers (> 0).
|
||||||
bail!(ErrorKind::InvalidLimit(n.to_string(), ValueType::Long));
|
bail!(AlgebrizerError::InvalidLimit(n.to_string(), ValueType::Long))
|
||||||
} else {
|
} else {
|
||||||
Some(Limit::Fixed(n as u64))
|
Some(Limit::Fixed(n as u64))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
// Same.
|
// Same.
|
||||||
bail!(ErrorKind::InvalidLimit(format!("{:?}", val), val.value_type()));
|
bail!(AlgebrizerError::InvalidLimit(format!("{:?}", val), val.value_type()))
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
// We know that the limit variable is mentioned in `:in`.
|
// We know that the limit variable is mentioned in `:in`.
|
||||||
|
@ -357,7 +357,7 @@ impl FindQuery {
|
||||||
|
|
||||||
for var in parsed.in_vars.into_iter() {
|
for var in parsed.in_vars.into_iter() {
|
||||||
if !set.insert(var.clone()) {
|
if !set.insert(var.clone()) {
|
||||||
bail!(ErrorKind::DuplicateVariableError(var.name(), ":in"));
|
bail!(AlgebrizerError::DuplicateVariableError(var.name(), ":in"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,7 +369,7 @@ impl FindQuery {
|
||||||
|
|
||||||
for var in parsed.with.into_iter() {
|
for var in parsed.with.into_iter() {
|
||||||
if !set.insert(var.clone()) {
|
if !set.insert(var.clone()) {
|
||||||
bail!(ErrorKind::DuplicateVariableError(var.name(), ":with"));
|
bail!(AlgebrizerError::DuplicateVariableError(var.name(), ":with"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,7 +379,7 @@ impl FindQuery {
|
||||||
// Make sure that if we have `:limit ?x`, `?x` appears in `:in`.
|
// Make sure that if we have `:limit ?x`, `?x` appears in `:in`.
|
||||||
if let Limit::Variable(ref v) = parsed.limit {
|
if let Limit::Variable(ref v) = parsed.limit {
|
||||||
if !in_vars.contains(v) {
|
if !in_vars.contains(v) {
|
||||||
bail!(ErrorKind::UnknownLimitVar(v.name()));
|
bail!(AlgebrizerError::UnknownLimitVar(v.name()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ use mentat_query::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
AlgebrizerError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ pub(crate) fn validate_or_join(or_join: &OrJoin) -> Result<()> {
|
||||||
let template = clauses.next().unwrap().collect_mentioned_variables();
|
let template = clauses.next().unwrap().collect_mentioned_variables();
|
||||||
for clause in clauses {
|
for clause in clauses {
|
||||||
if template != clause.collect_mentioned_variables() {
|
if template != clause.collect_mentioned_variables() {
|
||||||
bail!(ErrorKind::NonMatchingVariablesInOrClause);
|
bail!(AlgebrizerError::NonMatchingVariablesInOrClause)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -67,7 +67,7 @@ pub(crate) fn validate_or_join(or_join: &OrJoin) -> Result<()> {
|
||||||
let var_set: BTreeSet<Variable> = vars.iter().cloned().collect();
|
let var_set: BTreeSet<Variable> = vars.iter().cloned().collect();
|
||||||
for clause in &or_join.clauses {
|
for clause in &or_join.clauses {
|
||||||
if !var_set.is_subset(&clause.collect_mentioned_variables()) {
|
if !var_set.is_subset(&clause.collect_mentioned_variables()) {
|
||||||
bail!(ErrorKind::NonMatchingVariablesInOrClause);
|
bail!(AlgebrizerError::NonMatchingVariablesInOrClause)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -85,7 +85,7 @@ pub(crate) fn validate_not_join(not_join: &NotJoin) -> Result<()> {
|
||||||
// The joined vars must each appear somewhere in the clause's mentioned variables.
|
// The joined vars must each appear somewhere in the clause's mentioned variables.
|
||||||
let var_set: BTreeSet<Variable> = vars.iter().cloned().collect();
|
let var_set: BTreeSet<Variable> = vars.iter().cloned().collect();
|
||||||
if !var_set.is_subset(¬_join.collect_mentioned_variables()) {
|
if !var_set.is_subset(¬_join.collect_mentioned_variables()) {
|
||||||
bail!(ErrorKind::NonMatchingVariablesInNotClause);
|
bail!(AlgebrizerError::NonMatchingVariablesInNotClause)
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
|
|
@ -30,10 +30,10 @@ use mentat_query::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use mentat_query_algebrizer::{
|
use mentat_query_algebrizer::{
|
||||||
|
AlgebrizerError,
|
||||||
BindingError,
|
BindingError,
|
||||||
ComputedTable,
|
ComputedTable,
|
||||||
Error,
|
InvalidBinding,
|
||||||
ErrorKind,
|
|
||||||
Known,
|
Known,
|
||||||
QueryInputs,
|
QueryInputs,
|
||||||
};
|
};
|
||||||
|
@ -256,8 +256,8 @@ fn test_ground_coll_heterogeneous_types() {
|
||||||
let schema = prepopulated_schema();
|
let schema = prepopulated_schema();
|
||||||
let known = Known::for_schema(&schema);
|
let known = Known::for_schema(&schema);
|
||||||
let e = bails(known, &q);
|
let e = bails(known, &q);
|
||||||
match e {
|
match e.downcast().expect("proper error") {
|
||||||
Error(ErrorKind::InvalidGroundConstant, _) => {
|
AlgebrizerError::InvalidGroundConstant => {
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
panic!();
|
panic!();
|
||||||
|
@ -271,8 +271,8 @@ fn test_ground_rel_heterogeneous_types() {
|
||||||
let schema = prepopulated_schema();
|
let schema = prepopulated_schema();
|
||||||
let known = Known::for_schema(&schema);
|
let known = Known::for_schema(&schema);
|
||||||
let e = bails(known, &q);
|
let e = bails(known, &q);
|
||||||
match e {
|
match e.downcast().expect("proper error") {
|
||||||
Error(ErrorKind::InvalidGroundConstant, _) => {
|
AlgebrizerError::InvalidGroundConstant => {
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
panic!();
|
panic!();
|
||||||
|
@ -285,11 +285,10 @@ fn test_ground_tuple_duplicate_vars() {
|
||||||
let q = r#"[:find ?x :where [?x :foo/age ?v] [(ground [8 10]) [?x ?x]]]"#;
|
let q = r#"[:find ?x :where [?x :foo/age ?v] [(ground [8 10]) [?x ?x]]]"#;
|
||||||
let schema = prepopulated_schema();
|
let schema = prepopulated_schema();
|
||||||
let known = Known::for_schema(&schema);
|
let known = Known::for_schema(&schema);
|
||||||
let e = bails(known, &q);
|
let e: InvalidBinding = bails(known, &q).downcast().expect("proper error");
|
||||||
match e {
|
assert_eq!(e.function, PlainSymbol::plain("ground"));
|
||||||
Error(ErrorKind::InvalidBinding(v, e), _) => {
|
match e.inner.get_context() {
|
||||||
assert_eq!(v, PlainSymbol::plain("ground"));
|
&BindingError::RepeatedBoundVariable => {
|
||||||
assert_eq!(e, BindingError::RepeatedBoundVariable);
|
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
panic!();
|
panic!();
|
||||||
|
@ -302,11 +301,10 @@ fn test_ground_rel_duplicate_vars() {
|
||||||
let q = r#"[:find ?x :where [?x :foo/age ?v] [(ground [[8 10]]) [[?x ?x]]]]"#;
|
let q = r#"[:find ?x :where [?x :foo/age ?v] [(ground [[8 10]]) [[?x ?x]]]]"#;
|
||||||
let schema = prepopulated_schema();
|
let schema = prepopulated_schema();
|
||||||
let known = Known::for_schema(&schema);
|
let known = Known::for_schema(&schema);
|
||||||
let e = bails(known, &q);
|
let e: InvalidBinding = bails(known, &q).downcast().expect("expected InvalidBinding");
|
||||||
match e {
|
assert_eq!(e.function, PlainSymbol::plain("ground"));
|
||||||
Error(ErrorKind::InvalidBinding(v, e), _) => {
|
match e.inner.get_context() {
|
||||||
assert_eq!(v, PlainSymbol::plain("ground"));
|
&BindingError::RepeatedBoundVariable => {
|
||||||
assert_eq!(e, BindingError::RepeatedBoundVariable);
|
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
panic!();
|
panic!();
|
||||||
|
@ -319,9 +317,9 @@ fn test_ground_nonexistent_variable_invalid() {
|
||||||
let q = r#"[:find ?x ?e :where [?e _ ?x] (not [(ground 17) ?v])]"#;
|
let q = r#"[:find ?x ?e :where [?e _ ?x] (not [(ground 17) ?v])]"#;
|
||||||
let schema = prepopulated_schema();
|
let schema = prepopulated_schema();
|
||||||
let known = Known::for_schema(&schema);
|
let known = Known::for_schema(&schema);
|
||||||
let e = bails(known, &q);
|
let e = bails(known, &q).downcast().expect("proper error");
|
||||||
match e {
|
match e {
|
||||||
Error(ErrorKind::UnboundVariable(PlainSymbol(v)), _) => {
|
AlgebrizerError::UnboundVariable(PlainSymbol(v)) => {
|
||||||
assert_eq!(v, "?v".to_string());
|
assert_eq!(v, "?v".to_string());
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -343,9 +341,9 @@ fn test_unbound_input_variable_invalid() {
|
||||||
|
|
||||||
let i = QueryInputs::new(types, BTreeMap::default()).expect("valid QueryInputs");
|
let i = QueryInputs::new(types, BTreeMap::default()).expect("valid QueryInputs");
|
||||||
|
|
||||||
let e = bails_with_inputs(known, &q, i);
|
let e = bails_with_inputs(known, &q, i).downcast().expect("proper error");
|
||||||
match e {
|
match e {
|
||||||
Error(ErrorKind::UnboundVariable(v), _) => {
|
AlgebrizerError::UnboundVariable(v) => {
|
||||||
assert_eq!(v.0, "?x");
|
assert_eq!(v.0, "?x");
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -31,8 +31,8 @@ use mentat_query::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use mentat_query_algebrizer::{
|
use mentat_query_algebrizer::{
|
||||||
|
AlgebrizerError,
|
||||||
EmptyBecause,
|
EmptyBecause,
|
||||||
ErrorKind,
|
|
||||||
Known,
|
Known,
|
||||||
QueryInputs,
|
QueryInputs,
|
||||||
};
|
};
|
||||||
|
@ -78,8 +78,8 @@ fn test_instant_predicates_require_instants() {
|
||||||
:where
|
:where
|
||||||
[?e :foo/date ?t]
|
[?e :foo/date ?t]
|
||||||
[(> ?t "2017-06-16T00:56:41.257Z")]]"#;
|
[(> ?t "2017-06-16T00:56:41.257Z")]]"#;
|
||||||
match bails(known, query).0 {
|
match bails(known, query).downcast().expect("proper cause") {
|
||||||
ErrorKind::InvalidArgumentType(op, why, idx) => {
|
AlgebrizerError::InvalidArgumentType(op, why, idx) => {
|
||||||
assert_eq!(op, PlainSymbol::plain(">"));
|
assert_eq!(op, PlainSymbol::plain(">"));
|
||||||
assert_eq!(why, ValueTypeSet::of_numeric_and_instant_types());
|
assert_eq!(why, ValueTypeSet::of_numeric_and_instant_types());
|
||||||
assert_eq!(idx, 1);
|
assert_eq!(idx, 1);
|
||||||
|
@ -91,8 +91,8 @@ fn test_instant_predicates_require_instants() {
|
||||||
:where
|
:where
|
||||||
[?e :foo/date ?t]
|
[?e :foo/date ?t]
|
||||||
[(> "2017-06-16T00:56:41.257Z", ?t)]]"#;
|
[(> "2017-06-16T00:56:41.257Z", ?t)]]"#;
|
||||||
match bails(known, query).0 {
|
match bails(known, query).downcast().expect("proper cause") {
|
||||||
ErrorKind::InvalidArgumentType(op, why, idx) => {
|
AlgebrizerError::InvalidArgumentType(op, why, idx) => {
|
||||||
assert_eq!(op, PlainSymbol::plain(">"));
|
assert_eq!(op, PlainSymbol::plain(">"));
|
||||||
assert_eq!(why, ValueTypeSet::of_numeric_and_instant_types());
|
assert_eq!(why, ValueTypeSet::of_numeric_and_instant_types());
|
||||||
assert_eq!(idx, 0); // We get this right.
|
assert_eq!(idx, 0); // We get this right.
|
||||||
|
|
|
@ -13,6 +13,9 @@
|
||||||
// this module will get warnings otherwise).
|
// this module will get warnings otherwise).
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
extern crate failure;
|
||||||
|
use self::failure::Error;
|
||||||
|
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
Attribute,
|
Attribute,
|
||||||
Entid,
|
Entid,
|
||||||
|
@ -26,7 +29,6 @@ use mentat_query::{
|
||||||
|
|
||||||
use mentat_query_algebrizer::{
|
use mentat_query_algebrizer::{
|
||||||
ConjoiningClauses,
|
ConjoiningClauses,
|
||||||
Error,
|
|
||||||
Known,
|
Known,
|
||||||
QueryInputs,
|
QueryInputs,
|
||||||
algebrize,
|
algebrize,
|
||||||
|
|
|
@ -4,7 +4,8 @@ version = "0.0.1"
|
||||||
workspace = ".."
|
workspace = ".."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
|
failure = "0.1.1"
|
||||||
|
failure_derive = "0.1.1"
|
||||||
indexmap = "1"
|
indexmap = "1"
|
||||||
|
|
||||||
[dependencies.rusqlite]
|
[dependencies.rusqlite]
|
||||||
|
|
|
@ -33,7 +33,7 @@ use mentat_query_sql::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
ProjectorError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ impl SimpleAggregationOp {
|
||||||
pub(crate) fn is_applicable_to_types(&self, possibilities: ValueTypeSet) -> Result<ValueType> {
|
pub(crate) fn is_applicable_to_types(&self, possibilities: ValueTypeSet) -> Result<ValueType> {
|
||||||
use self::SimpleAggregationOp::*;
|
use self::SimpleAggregationOp::*;
|
||||||
if possibilities.is_empty() {
|
if possibilities.is_empty() {
|
||||||
bail!(ErrorKind::CannotProjectImpossibleBinding(*self))
|
bail!(ProjectorError::CannotProjectImpossibleBinding(*self))
|
||||||
}
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
|
@ -92,7 +92,7 @@ impl SimpleAggregationOp {
|
||||||
// The mean of a set of numeric values will always, for our purposes, be a double.
|
// The mean of a set of numeric values will always, for our purposes, be a double.
|
||||||
Ok(ValueType::Double)
|
Ok(ValueType::Double)
|
||||||
} else {
|
} else {
|
||||||
bail!(ErrorKind::CannotApplyAggregateOperationToTypes(*self, possibilities))
|
bail!(ProjectorError::CannotApplyAggregateOperationToTypes(*self, possibilities))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
&Sum => {
|
&Sum => {
|
||||||
|
@ -104,7 +104,7 @@ impl SimpleAggregationOp {
|
||||||
Ok(ValueType::Long)
|
Ok(ValueType::Long)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bail!(ErrorKind::CannotApplyAggregateOperationToTypes(*self, possibilities))
|
bail!(ProjectorError::CannotApplyAggregateOperationToTypes(*self, possibilities))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ impl SimpleAggregationOp {
|
||||||
|
|
||||||
// These types are unordered.
|
// These types are unordered.
|
||||||
Keyword | Ref | Uuid => {
|
Keyword | Ref | Uuid => {
|
||||||
bail!(ErrorKind::CannotApplyAggregateOperationToTypes(*self, possibilities))
|
bail!(ProjectorError::CannotApplyAggregateOperationToTypes(*self, possibilities))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -139,7 +139,7 @@ impl SimpleAggregationOp {
|
||||||
Ok(ValueType::Long)
|
Ok(ValueType::Long)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bail!(ErrorKind::CannotApplyAggregateOperationToTypes(*self, possibilities))
|
bail!(ProjectorError::CannotApplyAggregateOperationToTypes(*self, possibilities))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,72 +8,58 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
use rusqlite;
|
use std; // To refer to std::result::Result.
|
||||||
|
|
||||||
|
use failure::{
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
ValueTypeSet,
|
ValueTypeSet,
|
||||||
};
|
};
|
||||||
|
|
||||||
use mentat_db;
|
|
||||||
|
|
||||||
use mentat_query::{
|
use mentat_query::{
|
||||||
PlainSymbol,
|
PlainSymbol,
|
||||||
};
|
};
|
||||||
|
|
||||||
use mentat_query_pull;
|
|
||||||
|
|
||||||
use aggregates::{
|
use aggregates::{
|
||||||
SimpleAggregationOp,
|
SimpleAggregationOp,
|
||||||
};
|
};
|
||||||
|
|
||||||
error_chain! {
|
#[macro_export]
|
||||||
types {
|
macro_rules! bail {
|
||||||
Error, ErrorKind, ResultExt, Result;
|
($e:expr) => (
|
||||||
|
return Err($e.into());
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
errors {
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug, Fail)]
|
||||||
|
pub enum ProjectorError {
|
||||||
/// We're just not done yet. Message that the feature is recognized but not yet
|
/// We're just not done yet. Message that the feature is recognized but not yet
|
||||||
/// implemented.
|
/// implemented.
|
||||||
NotYetImplemented(t: String) {
|
#[fail(display = "not yet implemented: {}", _0)]
|
||||||
description("not yet implemented")
|
NotYetImplemented(String),
|
||||||
display("not yet implemented: {}", t)
|
|
||||||
}
|
|
||||||
CannotProjectImpossibleBinding(op: SimpleAggregationOp) {
|
|
||||||
description("no possible types for variable in projection list")
|
|
||||||
display("no possible types for value provided to {:?}", op)
|
|
||||||
}
|
|
||||||
CannotApplyAggregateOperationToTypes(op: SimpleAggregationOp, types: ValueTypeSet) {
|
|
||||||
description("cannot apply projection operation to types")
|
|
||||||
display("cannot apply projection operation {:?} to types {:?}", op, types)
|
|
||||||
}
|
|
||||||
InvalidProjection(t: String) {
|
|
||||||
description("invalid projection")
|
|
||||||
display("invalid projection: {}", t)
|
|
||||||
}
|
|
||||||
UnboundVariable(var: PlainSymbol) {
|
|
||||||
description("cannot project unbound variable")
|
|
||||||
display("cannot project unbound variable {:?}", var)
|
|
||||||
}
|
|
||||||
NoTypeAvailableForVariable(var: PlainSymbol) {
|
|
||||||
description("cannot find type for variable")
|
|
||||||
display("cannot find type for variable {:?}", var)
|
|
||||||
}
|
|
||||||
UnexpectedResultsType(actual: &'static str, expected: &'static str) {
|
|
||||||
description("unexpected query results type")
|
|
||||||
display("expected {}, got {}", expected, actual)
|
|
||||||
}
|
|
||||||
AmbiguousAggregates(min_max_count: usize, corresponding_count: usize) {
|
|
||||||
description("ambiguous aggregates")
|
|
||||||
display("min/max expressions: {} (max 1), corresponding: {}", min_max_count, corresponding_count)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreign_links {
|
#[fail(display = "no possible types for value provided to {:?}", _0)]
|
||||||
Rusqlite(rusqlite::Error);
|
CannotProjectImpossibleBinding(SimpleAggregationOp),
|
||||||
}
|
|
||||||
|
|
||||||
links {
|
#[fail(display = "cannot apply projection operation {:?} to types {:?}", _0, _1)]
|
||||||
DbError(mentat_db::Error, mentat_db::ErrorKind);
|
CannotApplyAggregateOperationToTypes(SimpleAggregationOp, ValueTypeSet),
|
||||||
PullError(mentat_query_pull::errors::Error, mentat_query_pull::errors::ErrorKind);
|
|
||||||
}
|
#[fail(display = "invalid projection: {}", _0)]
|
||||||
|
InvalidProjection(String),
|
||||||
|
|
||||||
|
#[fail(display = "cannot project unbound variable {:?}", _0)]
|
||||||
|
UnboundVariable(PlainSymbol),
|
||||||
|
|
||||||
|
#[fail(display = "cannot find type for variable {:?}", _0)]
|
||||||
|
NoTypeAvailableForVariable(PlainSymbol),
|
||||||
|
|
||||||
|
#[fail(display = "expected {}, got {}", _0, _1)]
|
||||||
|
UnexpectedResultsType(&'static str, &'static str),
|
||||||
|
|
||||||
|
#[fail(display = "min/max expressions: {} (max 1), corresponding: {}", _0, _1)]
|
||||||
|
AmbiguousAggregates(usize, usize),
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,10 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
extern crate failure;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate failure_derive;
|
||||||
extern crate indexmap;
|
extern crate indexmap;
|
||||||
extern crate rusqlite;
|
extern crate rusqlite;
|
||||||
|
|
||||||
|
@ -67,12 +69,14 @@ use mentat_query_sql::{
|
||||||
Projection,
|
Projection,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
pub mod errors;
|
||||||
|
|
||||||
mod aggregates;
|
mod aggregates;
|
||||||
mod project;
|
mod project;
|
||||||
mod projectors;
|
mod projectors;
|
||||||
mod pull;
|
mod pull;
|
||||||
mod relresult;
|
mod relresult;
|
||||||
pub mod errors;
|
|
||||||
|
|
||||||
pub use aggregates::{
|
pub use aggregates::{
|
||||||
SimpleAggregationOp,
|
SimpleAggregationOp,
|
||||||
|
@ -109,7 +113,7 @@ pub use relresult::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
ProjectorError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -296,35 +300,35 @@ impl QueryResults {
|
||||||
pub fn into_scalar(self) -> Result<Option<Binding>> {
|
pub fn into_scalar(self) -> Result<Option<Binding>> {
|
||||||
match self {
|
match self {
|
||||||
QueryResults::Scalar(o) => Ok(o),
|
QueryResults::Scalar(o) => Ok(o),
|
||||||
QueryResults::Coll(_) => bail!(ErrorKind::UnexpectedResultsType("coll", "scalar")),
|
QueryResults::Coll(_) => bail!(ProjectorError::UnexpectedResultsType("coll", "scalar")),
|
||||||
QueryResults::Tuple(_) => bail!(ErrorKind::UnexpectedResultsType("tuple", "scalar")),
|
QueryResults::Tuple(_) => bail!(ProjectorError::UnexpectedResultsType("tuple", "scalar")),
|
||||||
QueryResults::Rel(_) => bail!(ErrorKind::UnexpectedResultsType("rel", "scalar")),
|
QueryResults::Rel(_) => bail!(ProjectorError::UnexpectedResultsType("rel", "scalar")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_coll(self) -> Result<Vec<Binding>> {
|
pub fn into_coll(self) -> Result<Vec<Binding>> {
|
||||||
match self {
|
match self {
|
||||||
QueryResults::Scalar(_) => bail!(ErrorKind::UnexpectedResultsType("scalar", "coll")),
|
QueryResults::Scalar(_) => bail!(ProjectorError::UnexpectedResultsType("scalar", "coll")),
|
||||||
QueryResults::Coll(c) => Ok(c),
|
QueryResults::Coll(c) => Ok(c),
|
||||||
QueryResults::Tuple(_) => bail!(ErrorKind::UnexpectedResultsType("tuple", "coll")),
|
QueryResults::Tuple(_) => bail!(ProjectorError::UnexpectedResultsType("tuple", "coll")),
|
||||||
QueryResults::Rel(_) => bail!(ErrorKind::UnexpectedResultsType("rel", "coll")),
|
QueryResults::Rel(_) => bail!(ProjectorError::UnexpectedResultsType("rel", "coll")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_tuple(self) -> Result<Option<Vec<Binding>>> {
|
pub fn into_tuple(self) -> Result<Option<Vec<Binding>>> {
|
||||||
match self {
|
match self {
|
||||||
QueryResults::Scalar(_) => bail!(ErrorKind::UnexpectedResultsType("scalar", "tuple")),
|
QueryResults::Scalar(_) => bail!(ProjectorError::UnexpectedResultsType("scalar", "tuple")),
|
||||||
QueryResults::Coll(_) => bail!(ErrorKind::UnexpectedResultsType("coll", "tuple")),
|
QueryResults::Coll(_) => bail!(ProjectorError::UnexpectedResultsType("coll", "tuple")),
|
||||||
QueryResults::Tuple(t) => Ok(t),
|
QueryResults::Tuple(t) => Ok(t),
|
||||||
QueryResults::Rel(_) => bail!(ErrorKind::UnexpectedResultsType("rel", "tuple")),
|
QueryResults::Rel(_) => bail!(ProjectorError::UnexpectedResultsType("rel", "tuple")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_rel(self) -> Result<RelResult<Binding>> {
|
pub fn into_rel(self) -> Result<RelResult<Binding>> {
|
||||||
match self {
|
match self {
|
||||||
QueryResults::Scalar(_) => bail!(ErrorKind::UnexpectedResultsType("scalar", "rel")),
|
QueryResults::Scalar(_) => bail!(ProjectorError::UnexpectedResultsType("scalar", "rel")),
|
||||||
QueryResults::Coll(_) => bail!(ErrorKind::UnexpectedResultsType("coll", "rel")),
|
QueryResults::Coll(_) => bail!(ProjectorError::UnexpectedResultsType("coll", "rel")),
|
||||||
QueryResults::Tuple(_) => bail!(ErrorKind::UnexpectedResultsType("tuple", "rel")),
|
QueryResults::Tuple(_) => bail!(ProjectorError::UnexpectedResultsType("tuple", "rel")),
|
||||||
QueryResults::Rel(r) => Ok(r),
|
QueryResults::Rel(r) => Ok(r),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ use aggregates::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
ProjectorError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -127,14 +127,14 @@ fn candidate_type_column(cc: &ConjoiningClauses, var: &Variable) -> Result<(Colu
|
||||||
let type_name = VariableColumn::VariableTypeTag(var.clone()).column_name();
|
let type_name = VariableColumn::VariableTypeTag(var.clone()).column_name();
|
||||||
(ColumnOrExpression::Column(alias), type_name)
|
(ColumnOrExpression::Column(alias), type_name)
|
||||||
})
|
})
|
||||||
.ok_or_else(|| ErrorKind::UnboundVariable(var.name()).into())
|
.ok_or_else(|| ProjectorError::UnboundVariable(var.name()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cc_column(cc: &ConjoiningClauses, var: &Variable) -> Result<QualifiedAlias> {
|
fn cc_column(cc: &ConjoiningClauses, var: &Variable) -> Result<QualifiedAlias> {
|
||||||
cc.column_bindings
|
cc.column_bindings
|
||||||
.get(var)
|
.get(var)
|
||||||
.and_then(|cols| cols.get(0).cloned())
|
.and_then(|cols| cols.get(0).cloned())
|
||||||
.ok_or_else(|| ErrorKind::UnboundVariable(var.name()).into())
|
.ok_or_else(|| ProjectorError::UnboundVariable(var.name()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn candidate_column(cc: &ConjoiningClauses, var: &Variable) -> Result<(ColumnOrExpression, Name)> {
|
fn candidate_column(cc: &ConjoiningClauses, var: &Variable) -> Result<(ColumnOrExpression, Name)> {
|
||||||
|
@ -211,18 +211,18 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
|
||||||
match e {
|
match e {
|
||||||
&Element::Variable(ref var) => {
|
&Element::Variable(ref var) => {
|
||||||
if outer_variables.contains(var) {
|
if outer_variables.contains(var) {
|
||||||
bail!(ErrorKind::InvalidProjection(format!("Duplicate variable {} in query.", var)));
|
bail!(ProjectorError::InvalidProjection(format!("Duplicate variable {} in query.", var)));
|
||||||
}
|
}
|
||||||
if corresponded_variables.contains(var) {
|
if corresponded_variables.contains(var) {
|
||||||
bail!(ErrorKind::InvalidProjection(format!("Can't project both {} and `(the {})` from a query.", var, var)));
|
bail!(ProjectorError::InvalidProjection(format!("Can't project both {} and `(the {})` from a query.", var, var)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
&Element::Corresponding(ref var) => {
|
&Element::Corresponding(ref var) => {
|
||||||
if outer_variables.contains(var) {
|
if outer_variables.contains(var) {
|
||||||
bail!(ErrorKind::InvalidProjection(format!("Can't project both {} and `(the {})` from a query.", var, var)));
|
bail!(ProjectorError::InvalidProjection(format!("Can't project both {} and `(the {})` from a query.", var, var)));
|
||||||
}
|
}
|
||||||
if corresponded_variables.contains(var) {
|
if corresponded_variables.contains(var) {
|
||||||
bail!(ErrorKind::InvalidProjection(format!("`(the {})` appears twice in query.", var)));
|
bail!(ProjectorError::InvalidProjection(format!("`(the {})` appears twice in query.", var)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
&Element::Aggregate(_) => {
|
&Element::Aggregate(_) => {
|
||||||
|
@ -346,7 +346,7 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
|
||||||
i += 1;
|
i += 1;
|
||||||
} else {
|
} else {
|
||||||
// TODO: complex aggregates.
|
// TODO: complex aggregates.
|
||||||
bail!(ErrorKind::NotYetImplemented("complex aggregates".into()));
|
bail!(ProjectorError::NotYetImplemented("complex aggregates".into()));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -355,13 +355,13 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
|
||||||
match (min_max_count, corresponded_variables.len()) {
|
match (min_max_count, corresponded_variables.len()) {
|
||||||
(0, 0) | (_, 0) => {},
|
(0, 0) | (_, 0) => {},
|
||||||
(0, _) => {
|
(0, _) => {
|
||||||
bail!(ErrorKind::InvalidProjection("Warning: used `the` without `min` or `max`.".to_string()));
|
bail!(ProjectorError::InvalidProjection("Warning: used `the` without `min` or `max`.".to_string()));
|
||||||
},
|
},
|
||||||
(1, _) => {
|
(1, _) => {
|
||||||
// This is the success case!
|
// This is the success case!
|
||||||
},
|
},
|
||||||
(n, c) => {
|
(n, c) => {
|
||||||
bail!(ErrorKind::AmbiguousAggregates(n, c));
|
bail!(ProjectorError::AmbiguousAggregates(n, c));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,7 +465,7 @@ pub(crate) fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
|
||||||
.extracted_types
|
.extracted_types
|
||||||
.get(&var)
|
.get(&var)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| ErrorKind::NoTypeAvailableForVariable(var.name().clone()))?;
|
.ok_or_else(|| ProjectorError::NoTypeAvailableForVariable(var.name().clone()))?;
|
||||||
inner_projection.push(ProjectedColumn(ColumnOrExpression::Column(type_col), type_name.clone()));
|
inner_projection.push(ProjectedColumn(ColumnOrExpression::Column(type_col), type_name.clone()));
|
||||||
}
|
}
|
||||||
if group {
|
if group {
|
||||||
|
|
|
@ -30,9 +30,7 @@ use mentat_query_pull::{
|
||||||
Puller,
|
Puller,
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::Result;
|
||||||
Result,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Index,
|
Index,
|
||||||
|
|
|
@ -99,11 +99,10 @@ fn test_the_without_max_or_min() {
|
||||||
let projection = query_projection(&schema, &algebrized);
|
let projection = query_projection(&schema, &algebrized);
|
||||||
assert!(projection.is_err());
|
assert!(projection.is_err());
|
||||||
use ::mentat_query_projector::errors::{
|
use ::mentat_query_projector::errors::{
|
||||||
ErrorKind,
|
ProjectorError,
|
||||||
Error,
|
|
||||||
};
|
};
|
||||||
match projection {
|
match projection.err().expect("expected failure").downcast().expect("expected specific error") {
|
||||||
Result::Err(Error(ErrorKind::InvalidProjection(s) , _)) => {
|
ProjectorError::InvalidProjection(s) => {
|
||||||
assert_eq!(s.as_str(), "Warning: used `the` without `min` or `max`.");
|
assert_eq!(s.as_str(), "Warning: used `the` without `min` or `max`.");
|
||||||
},
|
},
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
|
|
@ -4,7 +4,8 @@ version = "0.0.1"
|
||||||
workspace = ".."
|
workspace = ".."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
|
failure = "0.1.1"
|
||||||
|
failure_derive = "0.1.1"
|
||||||
|
|
||||||
[dependencies.rusqlite]
|
[dependencies.rusqlite]
|
||||||
version = "0.13"
|
version = "0.13"
|
||||||
|
|
|
@ -8,28 +8,23 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
use std; // To refer to std::result::Result.
|
||||||
|
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
Entid,
|
Entid,
|
||||||
};
|
};
|
||||||
|
|
||||||
error_chain! {
|
use failure::{
|
||||||
types {
|
Error,
|
||||||
Error, ErrorKind, ResultExt, Result;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
errors {
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
UnnamedAttribute(id: Entid) {
|
|
||||||
description("unnamed attribute")
|
|
||||||
display("attribute {:?} has no name", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
RepeatedDbId {
|
#[derive(Debug, Fail)]
|
||||||
description(":db/id repeated")
|
pub enum PullError {
|
||||||
display(":db/id repeated")
|
#[fail(display = "attribute {:?} has no name", _0)]
|
||||||
}
|
UnnamedAttribute(Entid),
|
||||||
}
|
|
||||||
|
|
||||||
links {
|
#[fail(display = ":db/id repeated")]
|
||||||
DbError(::mentat_db::Error, ::mentat_db::ErrorKind);
|
RepeatedDbId,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,8 +57,10 @@
|
||||||
/// [*]))
|
/// [*]))
|
||||||
///! ```
|
///! ```
|
||||||
|
|
||||||
|
extern crate failure;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate failure_derive;
|
||||||
|
|
||||||
extern crate rusqlite;
|
extern crate rusqlite;
|
||||||
|
|
||||||
|
@ -101,7 +103,7 @@ use mentat_query::{
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
PullError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -163,7 +165,7 @@ impl Puller {
|
||||||
// In the unlikely event that we have an attribute with no name, we bail.
|
// In the unlikely event that we have an attribute with no name, we bail.
|
||||||
schema.get_ident(*i)
|
schema.get_ident(*i)
|
||||||
.map(|ident| ValueRc::new(ident.clone()))
|
.map(|ident| ValueRc::new(ident.clone()))
|
||||||
.ok_or_else(|| ErrorKind::UnnamedAttribute(*i))
|
.ok_or_else(|| PullError::UnnamedAttribute(*i))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut names: BTreeMap<Entid, ValueRc<Keyword>> = Default::default();
|
let mut names: BTreeMap<Entid, ValueRc<Keyword>> = Default::default();
|
||||||
|
@ -192,7 +194,7 @@ impl Puller {
|
||||||
&PullConcreteAttribute::Ident(ref i) if i.as_ref() == db_id.as_ref() => {
|
&PullConcreteAttribute::Ident(ref i) if i.as_ref() == db_id.as_ref() => {
|
||||||
// We only allow :db/id once.
|
// We only allow :db/id once.
|
||||||
if db_id_alias.is_some() {
|
if db_id_alias.is_some() {
|
||||||
bail!(ErrorKind::RepeatedDbId);
|
Err(PullError::RepeatedDbId)?
|
||||||
}
|
}
|
||||||
db_id_alias = Some(alias.unwrap_or_else(|| db_id.to_value_rc()));
|
db_id_alias = Some(alias.unwrap_or_else(|| db_id.to_value_rc()));
|
||||||
},
|
},
|
||||||
|
|
|
@ -42,6 +42,7 @@ use mentat_sql::{
|
||||||
BuildQueryResult,
|
BuildQueryResult,
|
||||||
QueryBuilder,
|
QueryBuilder,
|
||||||
QueryFragment,
|
QueryFragment,
|
||||||
|
SQLError,
|
||||||
SQLiteQueryBuilder,
|
SQLiteQueryBuilder,
|
||||||
SQLQuery,
|
SQLQuery,
|
||||||
};
|
};
|
||||||
|
@ -629,7 +630,7 @@ impl QueryFragment for SelectQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectQuery {
|
impl SelectQuery {
|
||||||
pub fn to_sql_query(&self) -> mentat_sql::Result<SQLQuery> {
|
pub fn to_sql_query(&self) -> Result<SQLQuery, SQLError> {
|
||||||
let mut builder = SQLiteQueryBuilder::new();
|
let mut builder = SQLiteQueryBuilder::new();
|
||||||
self.push_sql(&mut builder).map(|_| builder.finish())
|
self.push_sql(&mut builder).map(|_| builder.finish())
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@ version = "0.0.1"
|
||||||
workspace = ".."
|
workspace = ".."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
|
failure = "0.1.1"
|
||||||
|
failure_derive = "0.1.1"
|
||||||
|
|
||||||
[dependencies.mentat_core]
|
[dependencies.mentat_core]
|
||||||
path = "../core"
|
path = "../core"
|
||||||
|
|
|
@ -8,8 +8,7 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
#[macro_use]
|
extern crate failure;
|
||||||
extern crate error_chain;
|
|
||||||
extern crate mentat_core;
|
extern crate mentat_core;
|
||||||
extern crate mentat_query;
|
extern crate mentat_query;
|
||||||
extern crate mentat_query_algebrizer;
|
extern crate mentat_query_algebrizer;
|
||||||
|
@ -17,6 +16,8 @@ extern crate mentat_query_projector;
|
||||||
extern crate mentat_query_sql;
|
extern crate mentat_query_sql;
|
||||||
extern crate mentat_sql;
|
extern crate mentat_sql;
|
||||||
|
|
||||||
|
use failure::Error;
|
||||||
|
|
||||||
mod translate;
|
mod translate;
|
||||||
|
|
||||||
pub use mentat_query_sql::{
|
pub use mentat_query_sql::{
|
||||||
|
@ -29,15 +30,4 @@ pub use translate::{
|
||||||
query_to_select,
|
query_to_select,
|
||||||
};
|
};
|
||||||
|
|
||||||
error_chain! {
|
type Result<T> = std::result::Result<T, Error>;
|
||||||
types {
|
|
||||||
Error, ErrorKind, ResultExt, Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreign_links {
|
|
||||||
}
|
|
||||||
|
|
||||||
links {
|
|
||||||
ProjectorError(mentat_query_projector::errors::Error, mentat_query_projector::errors::ErrorKind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,7 +4,8 @@ version = "0.0.1"
|
||||||
workspace = ".."
|
workspace = ".."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
|
failure = "0.1.1"
|
||||||
|
failure_derive = "0.1.1"
|
||||||
ordered-float = "0.5"
|
ordered-float = "0.5"
|
||||||
|
|
||||||
[dependencies.rusqlite]
|
[dependencies.rusqlite]
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
extern crate failure;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use] extern crate failure_derive;
|
||||||
extern crate error_chain;
|
|
||||||
extern crate ordered_float;
|
extern crate ordered_float;
|
||||||
extern crate rusqlite;
|
extern crate rusqlite;
|
||||||
|
|
||||||
|
@ -29,25 +29,16 @@ use mentat_core::{
|
||||||
|
|
||||||
pub use rusqlite::types::Value;
|
pub use rusqlite::types::Value;
|
||||||
|
|
||||||
error_chain! {
|
#[derive(Debug, Fail)]
|
||||||
types {
|
pub enum SQLError {
|
||||||
Error, ErrorKind, ResultExt, Result;
|
#[fail(display = "invalid parameter name: {}", _0)]
|
||||||
|
InvalidParameterName(String),
|
||||||
|
|
||||||
|
#[fail(display = "parameter name could be generated: '{}'", _0)]
|
||||||
|
BindParamCouldBeGenerated(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
errors {
|
pub type BuildQueryResult = Result<(), SQLError>;
|
||||||
InvalidParameterName(name: String) {
|
|
||||||
description("invalid parameter name")
|
|
||||||
display("invalid parameter name: '{}'", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
BindParamCouldBeGenerated(name: String) {
|
|
||||||
description("parameter name could be generated")
|
|
||||||
display("parameter name could be generated: '{}'", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type BuildQueryResult = Result<()>;
|
|
||||||
|
|
||||||
/// We want to accumulate values that will later be substituted into a SQL statement execution.
|
/// We want to accumulate values that will later be substituted into a SQL statement execution.
|
||||||
/// This struct encapsulates the generated string and the _initial_ argument list.
|
/// This struct encapsulates the generated string and the _initial_ argument list.
|
||||||
|
@ -213,12 +204,12 @@ impl QueryBuilder for SQLiteQueryBuilder {
|
||||||
// Do some validation first.
|
// Do some validation first.
|
||||||
// This is not free, but it's probably worth it for now.
|
// This is not free, but it's probably worth it for now.
|
||||||
if !name.chars().all(|c| char::is_alphanumeric(c) || c == '_') {
|
if !name.chars().all(|c| char::is_alphanumeric(c) || c == '_') {
|
||||||
bail!(ErrorKind::InvalidParameterName(name.to_string()));
|
return Err(SQLError::InvalidParameterName(name.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if name.starts_with(self.arg_prefix.as_str()) &&
|
if name.starts_with(self.arg_prefix.as_str()) &&
|
||||||
name.chars().skip(self.arg_prefix.len()).all(char::is_numeric) {
|
name.chars().skip(self.arg_prefix.len()).all(char::is_numeric) {
|
||||||
bail!(ErrorKind::BindParamCouldBeGenerated(name.to_string()));
|
return Err(SQLError::BindParamCouldBeGenerated(name.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.push_sql("$");
|
self.push_sql("$");
|
||||||
|
|
50
src/conn.rs
50
src/conn.rs
|
@ -93,7 +93,10 @@ use entity_builder::{
|
||||||
TermBuilder,
|
TermBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::*;
|
use errors::{
|
||||||
|
Result,
|
||||||
|
MentatError,
|
||||||
|
};
|
||||||
|
|
||||||
use query::{
|
use query::{
|
||||||
Known,
|
Known,
|
||||||
|
@ -474,7 +477,7 @@ impl<'a, 'c> InProgress<'a, 'c> {
|
||||||
// Retrying is tracked by https://github.com/mozilla/mentat/issues/357.
|
// Retrying is tracked by https://github.com/mozilla/mentat/issues/357.
|
||||||
// This should not occur -- an attempt to take a competing IMMEDIATE transaction
|
// This should not occur -- an attempt to take a competing IMMEDIATE transaction
|
||||||
// will fail with `SQLITE_BUSY`, causing this function to abort.
|
// will fail with `SQLITE_BUSY`, causing this function to abort.
|
||||||
bail!("Lost the transact() race!");
|
bail!(MentatError::UnexpectedLostTransactRace);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit the SQLite transaction while we hold the mutex.
|
// Commit the SQLite transaction while we hold the mutex.
|
||||||
|
@ -506,7 +509,7 @@ impl<'a, 'c> InProgress<'a, 'c> {
|
||||||
cache_action: CacheAction) -> Result<()> {
|
cache_action: CacheAction) -> Result<()> {
|
||||||
let attribute_entid: Entid = self.schema
|
let attribute_entid: Entid = self.schema
|
||||||
.attribute_for_ident(&attribute)
|
.attribute_for_ident(&attribute)
|
||||||
.ok_or_else(|| ErrorKind::UnknownAttribute(attribute.to_string()))?.1.into();
|
.ok_or_else(|| MentatError::UnknownAttribute(attribute.to_string()))?.1.into();
|
||||||
|
|
||||||
match cache_action {
|
match cache_action {
|
||||||
CacheAction::Register => {
|
CacheAction::Register => {
|
||||||
|
@ -579,17 +582,16 @@ impl Conn {
|
||||||
/// Prepare the provided SQLite handle for use as a Mentat store. Creates tables but
|
/// Prepare the provided SQLite handle for use as a Mentat store. Creates tables but
|
||||||
/// _does not_ write the bootstrap schema. This constructor should only be used by
|
/// _does not_ write the bootstrap schema. This constructor should only be used by
|
||||||
/// consumers that expect to populate raw transaction data themselves.
|
/// consumers that expect to populate raw transaction data themselves.
|
||||||
|
|
||||||
pub(crate) fn empty(sqlite: &mut rusqlite::Connection) -> Result<Conn> {
|
pub(crate) fn empty(sqlite: &mut rusqlite::Connection) -> Result<Conn> {
|
||||||
let (tx, db) = db::create_empty_current_version(sqlite)
|
let (tx, db) = db::create_empty_current_version(sqlite)?;
|
||||||
.chain_err(|| "Unable to initialize Mentat store")?;
|
|
||||||
tx.commit()?;
|
tx.commit()?;
|
||||||
Ok(Conn::new(db.partition_map, db.schema))
|
Ok(Conn::new(db.partition_map, db.schema))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn connect(sqlite: &mut rusqlite::Connection) -> Result<Conn> {
|
pub fn connect(sqlite: &mut rusqlite::Connection) -> Result<Conn> {
|
||||||
let db = db::ensure_current_version(sqlite)
|
let db = db::ensure_current_version(sqlite)?;
|
||||||
.chain_err(|| "Unable to initialize Mentat store")?;
|
|
||||||
Ok(Conn::new(db.partition_map, db.schema))
|
Ok(Conn::new(db.partition_map, db.schema))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -801,7 +803,7 @@ impl Conn {
|
||||||
{
|
{
|
||||||
attribute_entid = metadata.schema
|
attribute_entid = metadata.schema
|
||||||
.attribute_for_ident(&attribute)
|
.attribute_for_ident(&attribute)
|
||||||
.ok_or_else(|| ErrorKind::UnknownAttribute(attribute.to_string()))?.1.into();
|
.ok_or_else(|| MentatError::UnknownAttribute(attribute.to_string()))?.1.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
let cache = &mut metadata.attribute_cache;
|
let cache = &mut metadata.attribute_cache;
|
||||||
|
@ -869,11 +871,11 @@ mod tests {
|
||||||
.partition_map[":db.part/user"].index;
|
.partition_map[":db.part/user"].index;
|
||||||
let t = format!("[[:db/add {} :db.schema/attribute \"tempid\"]]", next + 1);
|
let t = format!("[[:db/add {} :db.schema/attribute \"tempid\"]]", next + 1);
|
||||||
|
|
||||||
match conn.transact(&mut sqlite, t.as_str()).unwrap_err() {
|
match conn.transact(&mut sqlite, t.as_str()).expect_err("expected transact error").downcast() {
|
||||||
Error(ErrorKind::DbError(::mentat_db::errors::ErrorKind::UnrecognizedEntid(e)), _) => {
|
Ok(::mentat_db::DbError::UnrecognizedEntid(e)) => {
|
||||||
assert_eq!(e, next + 1);
|
assert_eq!(e, next + 1);
|
||||||
},
|
},
|
||||||
x => panic!("expected transact error, got {:?}", x),
|
x => panic!("expected db error, got {:?}", x),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transact two more tempids.
|
// Transact two more tempids.
|
||||||
|
@ -896,12 +898,12 @@ mod tests {
|
||||||
// we should reject this, because the first ID was provided by the user!
|
// we should reject this, because the first ID was provided by the user!
|
||||||
let t = format!("[[:db/add {} :db.schema/attribute \"tempid\"]]", next);
|
let t = format!("[[:db/add {} :db.schema/attribute \"tempid\"]]", next);
|
||||||
|
|
||||||
match conn.transact(&mut sqlite, t.as_str()).unwrap_err() {
|
match conn.transact(&mut sqlite, t.as_str()).expect_err("expected transact error").downcast() {
|
||||||
Error(ErrorKind::DbError(::mentat_db::errors::ErrorKind::UnrecognizedEntid(e)), _) => {
|
Ok(::mentat_db::DbError::UnrecognizedEntid(e)) => {
|
||||||
// All this, despite this being the ID we were about to allocate!
|
// All this, despite this being the ID we were about to allocate!
|
||||||
assert_eq!(e, next);
|
assert_eq!(e, next);
|
||||||
},
|
},
|
||||||
x => panic!("expected transact error, got {:?}", x),
|
x => panic!("expected db error, got {:?}", x),
|
||||||
}
|
}
|
||||||
|
|
||||||
// And if we subsequently transact in a way that allocates one ID, we _will_ use that one.
|
// And if we subsequently transact in a way that allocates one ID, we _will_ use that one.
|
||||||
|
@ -1057,9 +1059,9 @@ mod tests {
|
||||||
|
|
||||||
// Bad EDN: missing closing ']'.
|
// Bad EDN: missing closing ']'.
|
||||||
let report = conn.transact(&mut sqlite, "[[:db/add \"t\" :db/ident :a/keyword]");
|
let report = conn.transact(&mut sqlite, "[[:db/add \"t\" :db/ident :a/keyword]");
|
||||||
match report.unwrap_err() {
|
match report.expect_err("expected transact to fail for bad edn").downcast() {
|
||||||
Error(ErrorKind::EdnParseError(_), _) => { },
|
Ok(edn::ParseError { .. }) => { },
|
||||||
x => panic!("expected EDN parse error, got {:?}", x),
|
Err(x) => panic!("expected EDN parse error, got {:?}", x),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Good EDN.
|
// Good EDN.
|
||||||
|
@ -1068,9 +1070,9 @@ mod tests {
|
||||||
|
|
||||||
// Bad transaction data: missing leading :db/add.
|
// Bad transaction data: missing leading :db/add.
|
||||||
let report = conn.transact(&mut sqlite, "[[\"t\" :db/ident :b/keyword]]");
|
let report = conn.transact(&mut sqlite, "[[\"t\" :db/ident :b/keyword]]");
|
||||||
match report.unwrap_err() {
|
match report.expect_err("expected transact error").downcast() {
|
||||||
Error(ErrorKind::EdnParseError(_), _) => { },
|
Ok(edn::ParseError { .. }) => { },
|
||||||
x => panic!("expected EDN parse error, got {:?}", x),
|
Err(x) => panic!("expected EDN parse error, got {:?}", x),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Good transaction data.
|
// Good transaction data.
|
||||||
|
@ -1080,8 +1082,8 @@ mod tests {
|
||||||
// Bad transaction based on state of store: conflicting upsert.
|
// Bad transaction based on state of store: conflicting upsert.
|
||||||
let report = conn.transact(&mut sqlite, "[[:db/add \"u\" :db/ident :a/keyword]
|
let report = conn.transact(&mut sqlite, "[[:db/add \"u\" :db/ident :a/keyword]
|
||||||
[:db/add \"u\" :db/ident :b/keyword]]");
|
[:db/add \"u\" :db/ident :b/keyword]]");
|
||||||
match report.unwrap_err() {
|
match report.expect_err("expected transact error").downcast() {
|
||||||
Error(ErrorKind::DbError(::mentat_db::errors::ErrorKind::SchemaConstraintViolation(_)), _) => { },
|
Ok(::mentat_db::DbError::SchemaConstraintViolation(_)) => { },
|
||||||
x => panic!("expected schema constraint violation, got {:?}", x),
|
x => panic!("expected schema constraint violation, got {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1099,8 +1101,8 @@ mod tests {
|
||||||
let kw = kw!(:foo/bat);
|
let kw = kw!(:foo/bat);
|
||||||
let schema = conn.current_schema();
|
let schema = conn.current_schema();
|
||||||
let res = conn.cache(&mut sqlite, &schema, &kw, CacheDirection::Forward, CacheAction::Register);
|
let res = conn.cache(&mut sqlite, &schema, &kw, CacheDirection::Forward, CacheAction::Register);
|
||||||
match res.unwrap_err() {
|
match res.expect_err("expected cache to fail").downcast() {
|
||||||
Error(ErrorKind::UnknownAttribute(msg), _) => assert_eq!(msg, ":foo/bat"),
|
Ok(MentatError::UnknownAttribute(msg)) => assert_eq!(msg, ":foo/bat"),
|
||||||
x => panic!("expected UnknownAttribute error, got {:?}", x),
|
x => panic!("expected UnknownAttribute error, got {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ use conn::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
MentatError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -277,12 +277,12 @@ impl<'a, 'c> InProgressBuilder<'a, 'c> {
|
||||||
let provided = tv.value_type();
|
let provided = tv.value_type();
|
||||||
let expected = attr.value_type;
|
let expected = attr.value_type;
|
||||||
if provided != expected {
|
if provided != expected {
|
||||||
bail!(ErrorKind::ValueTypeMismatch(provided, expected));
|
bail!(MentatError::ValueTypeMismatch(provided, expected));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
attribute = aa;
|
attribute = aa;
|
||||||
} else {
|
} else {
|
||||||
bail!(ErrorKind::UnknownAttribute(a.to_string()));
|
bail!(MentatError::UnknownAttribute(a.to_string()));
|
||||||
}
|
}
|
||||||
Ok((attribute, v))
|
Ok((attribute, v))
|
||||||
}
|
}
|
||||||
|
@ -380,15 +380,8 @@ impl FromThing<KnownEntid> for TypedValueOr<TempIdHandle> {
|
||||||
mod testing {
|
mod testing {
|
||||||
extern crate mentat_db;
|
extern crate mentat_db;
|
||||||
|
|
||||||
use errors::{
|
|
||||||
Error,
|
|
||||||
ErrorKind,
|
|
||||||
};
|
|
||||||
|
|
||||||
// For matching inside a test.
|
// For matching inside a test.
|
||||||
use mentat_db::ErrorKind::{
|
use mentat_db::DbError;
|
||||||
UnrecognizedEntid,
|
|
||||||
};
|
|
||||||
|
|
||||||
use ::{
|
use ::{
|
||||||
Conn,
|
Conn,
|
||||||
|
@ -429,7 +422,7 @@ mod testing {
|
||||||
let mut in_progress = conn.begin_transaction(&mut sqlite).expect("begun successfully");
|
let mut in_progress = conn.begin_transaction(&mut sqlite).expect("begun successfully");
|
||||||
|
|
||||||
// This should fail: unrecognized entid.
|
// This should fail: unrecognized entid.
|
||||||
if let Err(Error(ErrorKind::DbError(UnrecognizedEntid(e)), _)) = in_progress.transact_terms(terms, tempids) {
|
if let Ok(DbError::UnrecognizedEntid(e)) = in_progress.transact_terms(terms, tempids).expect_err("expected transact to fail").downcast() {
|
||||||
assert_eq!(e, 999);
|
assert_eq!(e, 999);
|
||||||
} else {
|
} else {
|
||||||
panic!("Should have rejected the entid.");
|
panic!("Should have rejected the entid.");
|
||||||
|
|
113
src/errors.rs
113
src/errors.rs
|
@ -10,102 +10,63 @@
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use rusqlite;
|
use std; // To refer to std::result::Result.
|
||||||
|
|
||||||
use uuid;
|
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use edn;
|
use failure::Error;
|
||||||
|
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
Attribute,
|
Attribute,
|
||||||
ValueType,
|
ValueType,
|
||||||
};
|
};
|
||||||
use mentat_db;
|
|
||||||
use mentat_query;
|
use mentat_query;
|
||||||
use mentat_query_algebrizer;
|
|
||||||
use mentat_query_projector;
|
|
||||||
use mentat_query_pull;
|
|
||||||
use mentat_query_translator;
|
|
||||||
use mentat_sql;
|
|
||||||
use mentat_tolstoy;
|
|
||||||
|
|
||||||
error_chain! {
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
types {
|
|
||||||
Error, ErrorKind, ResultExt, Result;
|
#[macro_export]
|
||||||
|
macro_rules! bail {
|
||||||
|
($e:expr) => (
|
||||||
|
return Err($e.into());
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
foreign_links {
|
#[derive(Debug, Fail)]
|
||||||
EdnParseError(edn::ParseError);
|
pub enum MentatError {
|
||||||
Rusqlite(rusqlite::Error);
|
#[fail(display = "path {} already exists", _0)]
|
||||||
UuidParseError(uuid::ParseError);
|
PathAlreadyExists(String),
|
||||||
IoError(::std::io::Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
links {
|
#[fail(display = "variables {:?} unbound at query execution time", _0)]
|
||||||
DbError(mentat_db::Error, mentat_db::ErrorKind);
|
UnboundVariables(BTreeSet<String>),
|
||||||
QueryError(mentat_query_algebrizer::Error, mentat_query_algebrizer::ErrorKind); // Let's not leak the term 'algebrizer'.
|
|
||||||
ProjectorError(mentat_query_projector::errors::Error, mentat_query_projector::errors::ErrorKind);
|
|
||||||
PullError(mentat_query_pull::errors::Error, mentat_query_pull::errors::ErrorKind);
|
|
||||||
TranslatorError(mentat_query_translator::Error, mentat_query_translator::ErrorKind);
|
|
||||||
SqlError(mentat_sql::Error, mentat_sql::ErrorKind);
|
|
||||||
SyncError(mentat_tolstoy::Error, mentat_tolstoy::ErrorKind);
|
|
||||||
}
|
|
||||||
|
|
||||||
errors {
|
#[fail(display = "invalid argument name: '{}'", _0)]
|
||||||
PathAlreadyExists(path: String) {
|
InvalidArgumentName(String),
|
||||||
description("path already exists")
|
|
||||||
display("path {} already exists", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
UnboundVariables(names: BTreeSet<String>) {
|
#[fail(display = "unknown attribute: '{}'", _0)]
|
||||||
description("unbound variables at query execution time")
|
UnknownAttribute(String),
|
||||||
display("variables {:?} unbound at query execution time", names)
|
|
||||||
}
|
|
||||||
|
|
||||||
InvalidArgumentName(name: String) {
|
#[fail(display = "invalid vocabulary version")]
|
||||||
description("invalid argument name")
|
InvalidVocabularyVersion,
|
||||||
display("invalid argument name: '{}'", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
UnknownAttribute(name: String) {
|
#[fail(display = "vocabulary {}/{} already has attribute {}, and the requested definition differs", _0, _1, _2)]
|
||||||
description("unknown attribute")
|
ConflictingAttributeDefinitions(String, ::vocabulary::Version, String, Attribute, Attribute),
|
||||||
display("unknown attribute: '{}'", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
InvalidVocabularyVersion {
|
#[fail(display = "existing vocabulary {} too new: wanted {}, got {}", _0, _1, _2)]
|
||||||
description("invalid vocabulary version")
|
ExistingVocabularyTooNew(String, ::vocabulary::Version, ::vocabulary::Version),
|
||||||
display("invalid vocabulary version")
|
|
||||||
}
|
|
||||||
|
|
||||||
ConflictingAttributeDefinitions(vocabulary: String, version: ::vocabulary::Version, attribute: String, current: Attribute, requested: Attribute) {
|
#[fail(display = "core schema: wanted {}, got {:?}", _0, _1)]
|
||||||
description("conflicting attribute definitions")
|
UnexpectedCoreSchema(::vocabulary::Version, Option<::vocabulary::Version>),
|
||||||
display("vocabulary {}/{} already has attribute {}, and the requested definition differs", vocabulary, version, attribute)
|
|
||||||
}
|
|
||||||
|
|
||||||
ExistingVocabularyTooNew(name: String, existing: ::vocabulary::Version, ours: ::vocabulary::Version) {
|
#[fail(display = "Lost the transact() race!")]
|
||||||
description("existing vocabulary too new")
|
UnexpectedLostTransactRace,
|
||||||
display("existing vocabulary too new: wanted {}, got {}", ours, existing)
|
|
||||||
}
|
|
||||||
|
|
||||||
UnexpectedCoreSchema(version: Option<::vocabulary::Version>) {
|
#[fail(display = "missing core attribute {}", _0)]
|
||||||
description("unexpected core schema version")
|
MissingCoreVocabulary(mentat_query::Keyword),
|
||||||
display("core schema: wanted {}, got {:?}", mentat_db::CORE_SCHEMA_VERSION, version)
|
|
||||||
}
|
|
||||||
|
|
||||||
MissingCoreVocabulary(kw: mentat_query::Keyword) {
|
#[fail(display = "schema changed since query was prepared")]
|
||||||
description("missing core vocabulary")
|
PreparedQuerySchemaMismatch,
|
||||||
display("missing core attribute {}", kw)
|
|
||||||
}
|
|
||||||
|
|
||||||
PreparedQuerySchemaMismatch {
|
#[fail(display = "provided value of type {} doesn't match attribute value type {}", _0, _1)]
|
||||||
description("schema changed since query was prepared")
|
ValueTypeMismatch(ValueType, ValueType),
|
||||||
display("schema changed since query was prepared")
|
|
||||||
}
|
|
||||||
|
|
||||||
ValueTypeMismatch(provided: ValueType, expected: ValueType) {
|
|
||||||
description("provided value doesn't match value type")
|
|
||||||
display("provided value of type {} doesn't match attribute value type {}", provided, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
#![recursion_limit="128"]
|
#![recursion_limit="128"]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate failure_derive;
|
||||||
|
extern crate failure;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
@ -101,9 +102,10 @@ macro_rules! kw {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
pub mod errors;
|
||||||
pub mod conn;
|
pub mod conn;
|
||||||
pub mod entity_builder;
|
pub mod entity_builder;
|
||||||
pub mod errors;
|
|
||||||
pub mod ident;
|
pub mod ident;
|
||||||
pub mod query;
|
pub mod query;
|
||||||
pub mod query_builder;
|
pub mod query_builder;
|
||||||
|
|
|
@ -74,7 +74,7 @@ pub use mentat_query_projector::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
MentatError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ fn algebrize_query<T>
|
||||||
// If they aren't, the user has made an error -- perhaps writing the wrong variable in `:in`, or
|
// If they aren't, the user has made an error -- perhaps writing the wrong variable in `:in`, or
|
||||||
// not binding in the `QueryInput`.
|
// not binding in the `QueryInput`.
|
||||||
if !unbound.is_empty() {
|
if !unbound.is_empty() {
|
||||||
bail!(ErrorKind::UnboundVariables(unbound.into_iter().map(|v| v.to_string()).collect()));
|
bail!(MentatError::UnboundVariables(unbound.into_iter().map(|v| v.to_string()).collect()));
|
||||||
}
|
}
|
||||||
Ok(algebrized)
|
Ok(algebrized)
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ fn fetch_values<'sqlite>
|
||||||
|
|
||||||
fn lookup_attribute(schema: &Schema, attribute: &Keyword) -> Result<KnownEntid> {
|
fn lookup_attribute(schema: &Schema, attribute: &Keyword) -> Result<KnownEntid> {
|
||||||
schema.get_entid(attribute)
|
schema.get_entid(attribute)
|
||||||
.ok_or_else(|| ErrorKind::UnknownAttribute(attribute.name().into()).into())
|
.ok_or_else(|| MentatError::UnknownAttribute(attribute.name().into()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a single value for the provided entity and attribute.
|
/// Return a single value for the provided entity and attribute.
|
||||||
|
@ -398,7 +398,7 @@ pub fn q_prepare<'sqlite, 'schema, 'cache, 'query, T>
|
||||||
if !unbound.is_empty() {
|
if !unbound.is_empty() {
|
||||||
// TODO: Allow binding variables at execution time, not just
|
// TODO: Allow binding variables at execution time, not just
|
||||||
// preparation time.
|
// preparation time.
|
||||||
bail!(ErrorKind::UnboundVariables(unbound.into_iter().map(|v| v.to_string()).collect()));
|
bail!(MentatError::UnboundVariables(unbound.into_iter().map(|v| v.to_string()).collect()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if algebrized.is_known_empty() {
|
if algebrized.is_known_empty() {
|
||||||
|
|
|
@ -34,7 +34,7 @@ use ::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
MentatError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ impl<'a> QueryBuilder<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind_ref_from_kw(&mut self, var: &str, value: Keyword) -> Result<&mut Self> {
|
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(ErrorKind::UnknownAttribute(value.to_string()))?;
|
let entid = self.store.conn().current_schema().get_entid(&value).ok_or(MentatError::UnknownAttribute(value.to_string()))?;
|
||||||
self.values.insert(Variable::from_valid_name(var), TypedValue::Ref(entid.into()));
|
self.values.insert(Variable::from_valid_name(var), TypedValue::Ref(entid.into()));
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ impl Store {
|
||||||
pub fn open_empty(path: &str) -> Result<Store> {
|
pub fn open_empty(path: &str) -> Result<Store> {
|
||||||
if !path.is_empty() {
|
if !path.is_empty() {
|
||||||
if Path::new(path).exists() {
|
if Path::new(path).exists() {
|
||||||
bail!(ErrorKind::PathAlreadyExists(path.to_string()));
|
bail!(MentatError::PathAlreadyExists(path.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ impl Store {
|
||||||
pub fn open_empty_with_key(path: &str, encryption_key: &str) -> Result<Store> {
|
pub fn open_empty_with_key(path: &str, encryption_key: &str) -> Result<Store> {
|
||||||
if !path.is_empty() {
|
if !path.is_empty() {
|
||||||
if Path::new(path).exists() {
|
if Path::new(path).exists() {
|
||||||
bail!(ErrorKind::PathAlreadyExists(path.to_string()));
|
bail!(MentatError::PathAlreadyExists(path.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,7 @@ use ::conn::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use ::errors::{
|
use ::errors::{
|
||||||
ErrorKind,
|
MentatError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -375,17 +375,17 @@ trait HasCoreSchema {
|
||||||
impl<T> HasCoreSchema for T where T: HasSchema {
|
impl<T> HasCoreSchema for T where T: HasSchema {
|
||||||
fn core_type(&self, t: ValueType) -> Result<KnownEntid> {
|
fn core_type(&self, t: ValueType) -> Result<KnownEntid> {
|
||||||
self.entid_for_type(t)
|
self.entid_for_type(t)
|
||||||
.ok_or_else(|| ErrorKind::MissingCoreVocabulary(DB_SCHEMA_VERSION.clone()).into())
|
.ok_or_else(|| MentatError::MissingCoreVocabulary(DB_SCHEMA_VERSION.clone()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn core_entid(&self, ident: &Keyword) -> Result<KnownEntid> {
|
fn core_entid(&self, ident: &Keyword) -> Result<KnownEntid> {
|
||||||
self.get_entid(ident)
|
self.get_entid(ident)
|
||||||
.ok_or_else(|| ErrorKind::MissingCoreVocabulary(DB_SCHEMA_VERSION.clone()).into())
|
.ok_or_else(|| MentatError::MissingCoreVocabulary(DB_SCHEMA_VERSION.clone()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn core_attribute(&self, ident: &Keyword) -> Result<KnownEntid> {
|
fn core_attribute(&self, ident: &Keyword) -> Result<KnownEntid> {
|
||||||
self.attribute_for_ident(ident)
|
self.attribute_for_ident(ident)
|
||||||
.ok_or_else(|| ErrorKind::MissingCoreVocabulary(DB_SCHEMA_VERSION.clone()).into())
|
.ok_or_else(|| MentatError::MissingCoreVocabulary(DB_SCHEMA_VERSION.clone()).into())
|
||||||
.map(|(_, e)| e)
|
.map(|(_, e)| e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -568,7 +568,7 @@ pub trait VersionedStore: HasVocabularies + HasSchema {
|
||||||
// We have two vocabularies with the same name, same version, and
|
// We have two vocabularies with the same name, same version, and
|
||||||
// different definitions for an attribute. That's a coding error.
|
// different definitions for an attribute. That's a coding error.
|
||||||
// We can't accept this vocabulary.
|
// We can't accept this vocabulary.
|
||||||
bail!(ErrorKind::ConflictingAttributeDefinitions(
|
bail!(MentatError::ConflictingAttributeDefinitions(
|
||||||
definition.name.to_string(),
|
definition.name.to_string(),
|
||||||
definition.version,
|
definition.version,
|
||||||
pair.0.to_string(),
|
pair.0.to_string(),
|
||||||
|
@ -615,13 +615,13 @@ pub trait VersionedStore: HasVocabularies + HasSchema {
|
||||||
fn verify_core_schema(&self) -> Result<()> {
|
fn verify_core_schema(&self) -> Result<()> {
|
||||||
if let Some(core) = self.read_vocabulary_named(&DB_SCHEMA_CORE)? {
|
if let Some(core) = self.read_vocabulary_named(&DB_SCHEMA_CORE)? {
|
||||||
if core.version != CORE_SCHEMA_VERSION {
|
if core.version != CORE_SCHEMA_VERSION {
|
||||||
bail!(ErrorKind::UnexpectedCoreSchema(Some(core.version)));
|
bail!(MentatError::UnexpectedCoreSchema(CORE_SCHEMA_VERSION, Some(core.version)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check things other than the version.
|
// TODO: check things other than the version.
|
||||||
} else {
|
} else {
|
||||||
// This would be seriously messed up.
|
// This would be seriously messed up.
|
||||||
bail!(ErrorKind::UnexpectedCoreSchema(None));
|
bail!(MentatError::UnexpectedCoreSchema(CORE_SCHEMA_VERSION, None));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -682,7 +682,7 @@ impl<'a, 'c> VersionedStore for InProgress<'a, 'c> {
|
||||||
VocabularyCheck::NotPresent => self.install_vocabulary(definition),
|
VocabularyCheck::NotPresent => self.install_vocabulary(definition),
|
||||||
VocabularyCheck::PresentButNeedsUpdate { older_version } => self.upgrade_vocabulary(definition, older_version),
|
VocabularyCheck::PresentButNeedsUpdate { older_version } => self.upgrade_vocabulary(definition, older_version),
|
||||||
VocabularyCheck::PresentButMissingAttributes { attributes } => self.install_attributes_for(definition, attributes),
|
VocabularyCheck::PresentButMissingAttributes { attributes } => self.install_attributes_for(definition, attributes),
|
||||||
VocabularyCheck::PresentButTooNew { newer_version } => Err(ErrorKind::ExistingVocabularyTooNew(definition.name.to_string(), newer_version.version, definition.version).into()),
|
VocabularyCheck::PresentButTooNew { newer_version } => Err(MentatError::ExistingVocabularyTooNew(definition.name.to_string(), newer_version.version, definition.version).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,7 +701,7 @@ impl<'a, 'c> VersionedStore for InProgress<'a, 'c> {
|
||||||
out.insert(definition.name.clone(), VocabularyOutcome::Existed);
|
out.insert(definition.name.clone(), VocabularyOutcome::Existed);
|
||||||
},
|
},
|
||||||
VocabularyCheck::PresentButTooNew { newer_version } => {
|
VocabularyCheck::PresentButTooNew { newer_version } => {
|
||||||
bail!(ErrorKind::ExistingVocabularyTooNew(definition.name.to_string(), newer_version.version, definition.version));
|
bail!(MentatError::ExistingVocabularyTooNew(definition.name.to_string(), newer_version.version, definition.version));
|
||||||
},
|
},
|
||||||
|
|
||||||
c @ VocabularyCheck::NotPresent |
|
c @ VocabularyCheck::NotPresent |
|
||||||
|
@ -868,7 +868,7 @@ impl<T> HasVocabularies for T where T: HasSchema + Queryable {
|
||||||
attributes: attributes,
|
attributes: attributes,
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
Some(_) => bail!(ErrorKind::InvalidVocabularyVersion),
|
Some(_) => bail!(MentatError::InvalidVocabularyVersion),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|
|
@ -61,8 +61,7 @@ use mentat::query::q_uncached;
|
||||||
use mentat::conn::Conn;
|
use mentat::conn::Conn;
|
||||||
|
|
||||||
use mentat::errors::{
|
use mentat::errors::{
|
||||||
Error,
|
MentatError,
|
||||||
ErrorKind,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -234,11 +233,11 @@ fn test_unbound_inputs() {
|
||||||
let results = q_uncached(&c, &db.schema,
|
let results = q_uncached(&c, &db.schema,
|
||||||
"[:find ?i . :in ?e :where [?e :db/ident ?i]]", inputs);
|
"[:find ?i . :in ?e :where [?e :db/ident ?i]]", inputs);
|
||||||
|
|
||||||
match results {
|
match results.expect_err("expected unbound variables").downcast().expect("expected specific error") {
|
||||||
Result::Err(Error(ErrorKind::UnboundVariables(vars), _)) => {
|
MentatError::UnboundVariables(vars) => {
|
||||||
assert_eq!(vars, vec!["?e".to_string()].into_iter().collect());
|
assert_eq!(vars, vec!["?e".to_string()].into_iter().collect());
|
||||||
},
|
},
|
||||||
_ => panic!("Expected unbound variables."),
|
_ => panic!("Expected UnboundVariables variant."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,8 +411,8 @@ fn test_fulltext() {
|
||||||
[?a :foo/term ?term]
|
[?a :foo/term ?term]
|
||||||
]"#;
|
]"#;
|
||||||
let r = conn.q_once(&mut c, query, None);
|
let r = conn.q_once(&mut c, query, None);
|
||||||
match r {
|
match r.expect_err("expected query to fail").downcast() {
|
||||||
Err(Error(ErrorKind::QueryError(mentat_query_algebrizer::ErrorKind::InvalidArgument(PlainSymbol(s), ty, i)), _)) => {
|
Ok(mentat_query_algebrizer::AlgebrizerError::InvalidArgument(PlainSymbol(s), ty, i)) => {
|
||||||
assert_eq!(s, "fulltext");
|
assert_eq!(s, "fulltext");
|
||||||
assert_eq!(ty, "string");
|
assert_eq!(ty, "string");
|
||||||
assert_eq!(i, 2);
|
assert_eq!(i, 2);
|
||||||
|
@ -427,8 +426,8 @@ fn test_fulltext() {
|
||||||
[?a :foo/term ?term]
|
[?a :foo/term ?term]
|
||||||
[(fulltext $ :foo/fts ?a) [[?x ?val]]]]"#;
|
[(fulltext $ :foo/fts ?a) [[?x ?val]]]]"#;
|
||||||
let r = conn.q_once(&mut c, query, None);
|
let r = conn.q_once(&mut c, query, None);
|
||||||
match r {
|
match r.expect_err("expected query to fail").downcast() {
|
||||||
Err(Error(ErrorKind::QueryError(mentat_query_algebrizer::ErrorKind::InvalidArgument(PlainSymbol(s), ty, i)), _)) => {
|
Ok(mentat_query_algebrizer::AlgebrizerError::InvalidArgument(PlainSymbol(s), ty, i)) => {
|
||||||
assert_eq!(s, "fulltext");
|
assert_eq!(s, "fulltext");
|
||||||
assert_eq!(ty, "string");
|
assert_eq!(ty, "string");
|
||||||
assert_eq!(i, 2);
|
assert_eq!(i, 2);
|
||||||
|
@ -583,42 +582,25 @@ fn test_aggregates_type_handling() {
|
||||||
// No type limits => can't do it.
|
// No type limits => can't do it.
|
||||||
let r = store.q_once(r#"[:find (sum ?v) . :where [_ _ ?v]]"#, None);
|
let r = store.q_once(r#"[:find (sum ?v) . :where [_ _ ?v]]"#, None);
|
||||||
let all_types = ValueTypeSet::any();
|
let all_types = ValueTypeSet::any();
|
||||||
match r {
|
match r.expect_err("expected query to fail").downcast() {
|
||||||
Result::Err(
|
Ok(::mentat_query_projector::errors::ProjectorError::CannotApplyAggregateOperationToTypes(
|
||||||
Error(
|
SimpleAggregationOp::Sum, types)) => {
|
||||||
ErrorKind::TranslatorError(
|
|
||||||
::mentat_query_translator::ErrorKind::ProjectorError(
|
|
||||||
::mentat_query_projector::errors::ErrorKind::CannotApplyAggregateOperationToTypes(
|
|
||||||
SimpleAggregationOp::Sum,
|
|
||||||
types
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
_)) => {
|
|
||||||
assert_eq!(types, all_types);
|
assert_eq!(types, all_types);
|
||||||
},
|
},
|
||||||
r => panic!("Unexpected: {:?}", r),
|
e => panic!("Unexpected error type {:?}", e),
|
||||||
}
|
}
|
||||||
|
|
||||||
// You can't sum instants.
|
// You can't sum instants.
|
||||||
let r = store.q_once(r#"[:find (sum ?v) .
|
let r = store.q_once(r#"[:find (sum ?v) .
|
||||||
:where [_ _ ?v] [(type ?v :db.type/instant)]]"#,
|
:where [_ _ ?v] [(type ?v :db.type/instant)]]"#,
|
||||||
None);
|
None);
|
||||||
match r {
|
match r.expect_err("expected query to fail").downcast() {
|
||||||
Result::Err(
|
Ok(::mentat_query_projector::errors::ProjectorError::CannotApplyAggregateOperationToTypes(
|
||||||
Error(
|
|
||||||
ErrorKind::TranslatorError(
|
|
||||||
::mentat_query_translator::ErrorKind::ProjectorError(
|
|
||||||
::mentat_query_projector::errors::ErrorKind::CannotApplyAggregateOperationToTypes(
|
|
||||||
SimpleAggregationOp::Sum,
|
SimpleAggregationOp::Sum,
|
||||||
types
|
types)) => {
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
_)) => {
|
|
||||||
assert_eq!(types, ValueTypeSet::of_one(ValueType::Instant));
|
assert_eq!(types, ValueTypeSet::of_one(ValueType::Instant));
|
||||||
},
|
},
|
||||||
r => panic!("Unexpected: {:?}", r),
|
e => panic!("Unexpected error type {:?}", e),
|
||||||
}
|
}
|
||||||
|
|
||||||
// But you can count them.
|
// But you can count them.
|
||||||
|
@ -1354,19 +1336,13 @@ fn test_aggregation_implicit_grouping() {
|
||||||
[?person :foo/play ?game]
|
[?person :foo/play ?game]
|
||||||
[?person :foo/is-vegetarian true]
|
[?person :foo/is-vegetarian true]
|
||||||
[?person :foo/name ?name]]"#, None);
|
[?person :foo/name ?name]]"#, None);
|
||||||
match res {
|
match res.expect_err("expected query to fail").downcast() {
|
||||||
Result::Err(
|
Ok(::mentat_query_projector::errors::ProjectorError::AmbiguousAggregates(mmc, cc)) => {
|
||||||
Error(
|
|
||||||
ErrorKind::TranslatorError(
|
|
||||||
::mentat_query_translator::ErrorKind::ProjectorError(
|
|
||||||
::mentat_query_projector::errors::ErrorKind::AmbiguousAggregates(mmc, cc)
|
|
||||||
)
|
|
||||||
), _)) => {
|
|
||||||
assert_eq!(mmc, 2);
|
assert_eq!(mmc, 2);
|
||||||
assert_eq!(cc, 1);
|
assert_eq!(cc, 1);
|
||||||
},
|
},
|
||||||
r => {
|
e => {
|
||||||
panic!("Unexpected result {:?}.", r);
|
panic!("Unexpected error type {:?}.", e);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,10 +59,7 @@ use mentat::entity_builder::{
|
||||||
TermBuilder,
|
TermBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
use mentat::errors::{
|
use mentat::errors::MentatError;
|
||||||
Error,
|
|
||||||
ErrorKind,
|
|
||||||
};
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref FOO_NAME: Keyword = {
|
static ref FOO_NAME: Keyword = {
|
||||||
|
@ -289,8 +286,8 @@ fn test_add_vocab() {
|
||||||
// Scoped borrow of `conn`.
|
// Scoped borrow of `conn`.
|
||||||
{
|
{
|
||||||
let mut in_progress = conn.begin_transaction(&mut sqlite).expect("begun successfully");
|
let mut in_progress = conn.begin_transaction(&mut sqlite).expect("begun successfully");
|
||||||
match in_progress.ensure_vocabulary(&foo_v1_malformed) {
|
match in_progress.ensure_vocabulary(&foo_v1_malformed).expect_err("expected vocabulary to fail").downcast() {
|
||||||
Result::Err(Error(ErrorKind::ConflictingAttributeDefinitions(vocab, version, attr, theirs, ours), _)) => {
|
Ok(MentatError::ConflictingAttributeDefinitions(vocab, version, attr, theirs, ours)) => {
|
||||||
assert_eq!(vocab.as_str(), ":org.mozilla/foo");
|
assert_eq!(vocab.as_str(), ":org.mozilla/foo");
|
||||||
assert_eq!(attr.as_str(), ":foo/baz");
|
assert_eq!(attr.as_str(), ":foo/baz");
|
||||||
assert_eq!(version, 1);
|
assert_eq!(version, 1);
|
||||||
|
|
|
@ -5,6 +5,8 @@ workspace = ".."
|
||||||
authors = ["Grisha Kruglov <gkruglov@mozilla.com>"]
|
authors = ["Grisha Kruglov <gkruglov@mozilla.com>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
failure = "0.1.1"
|
||||||
|
failure_derive = "0.1.1"
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
hyper = "0.11"
|
hyper = "0.11"
|
||||||
tokio-core = "0.1"
|
tokio-core = "0.1"
|
||||||
|
@ -15,8 +17,6 @@ serde_derive = "1.0"
|
||||||
lazy_static = "0.2"
|
lazy_static = "0.2"
|
||||||
uuid = { version = "0.5", features = ["v4", "serde"] }
|
uuid = { version = "0.5", features = ["v4", "serde"] }
|
||||||
|
|
||||||
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
|
|
||||||
|
|
||||||
[dependencies.mentat_core]
|
[dependencies.mentat_core]
|
||||||
path = "../core"
|
path = "../core"
|
||||||
|
|
||||||
|
|
|
@ -10,63 +10,34 @@
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std;
|
use failure::Error;
|
||||||
use hyper;
|
|
||||||
use rusqlite;
|
|
||||||
use uuid;
|
|
||||||
use mentat_db;
|
|
||||||
use serde_cbor;
|
|
||||||
use serde_json;
|
|
||||||
|
|
||||||
error_chain! {
|
#[macro_export]
|
||||||
types {
|
macro_rules! bail {
|
||||||
Error, ErrorKind, ResultExt, Result;
|
($e:expr) => (
|
||||||
|
return Err($e.into());
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
foreign_links {
|
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||||
IOError(std::io::Error);
|
|
||||||
HttpError(hyper::Error);
|
|
||||||
HyperUriError(hyper::error::UriError);
|
|
||||||
SqlError(rusqlite::Error);
|
|
||||||
UuidParseError(uuid::ParseError);
|
|
||||||
Utf8Error(std::str::Utf8Error);
|
|
||||||
JsonError(serde_json::Error);
|
|
||||||
CborError(serde_cbor::error::Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
links {
|
#[derive(Debug, Fail)]
|
||||||
DbError(mentat_db::Error, mentat_db::ErrorKind);
|
pub enum TolstoyError {
|
||||||
}
|
#[fail(display = "Received bad response from the server: {}", _0)]
|
||||||
|
BadServerResponse(String),
|
||||||
|
|
||||||
errors {
|
#[fail(display = "encountered more than one metadata value for key: {}", _0)]
|
||||||
TxIncorrectlyMapped(n: usize) {
|
DuplicateMetadata(String),
|
||||||
description("encountered more than one uuid mapping for tx")
|
|
||||||
display("expected one, found {} uuid mappings for tx", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
UnexpectedState(t: String) {
|
#[fail(display = "transaction processor didn't say it was done")]
|
||||||
description("encountered unexpected state")
|
TxProcessorUnfinished,
|
||||||
display("encountered unexpected state: {}", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
NotYetImplemented(t: String) {
|
#[fail(display = "expected one, found {} uuid mappings for tx", _0)]
|
||||||
description("not yet implemented")
|
TxIncorrectlyMapped(usize),
|
||||||
display("not yet implemented: {}", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
DuplicateMetadata(k: String) {
|
#[fail(display = "encountered unexpected state: {}", _0)]
|
||||||
description("encountered more than one metadata value for key")
|
UnexpectedState(String),
|
||||||
display("encountered more than one metadata value for key: {}", k)
|
|
||||||
}
|
|
||||||
|
|
||||||
TxProcessorUnfinished {
|
#[fail(display = "not yet implemented: {}", _0)]
|
||||||
description("Tx processor couldn't finish")
|
NotYetImplemented(String),
|
||||||
display("Tx processor couldn't finish")
|
|
||||||
}
|
|
||||||
|
|
||||||
BadServerResponse(s: String) {
|
|
||||||
description("Received bad response from the server")
|
|
||||||
display("Received bad response from the server: {}", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,9 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
// For error_chain:
|
extern crate failure;
|
||||||
#![recursion_limit="128"]
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate failure_derive;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
@ -33,16 +31,15 @@ extern crate mentat_core;
|
||||||
extern crate rusqlite;
|
extern crate rusqlite;
|
||||||
extern crate uuid;
|
extern crate uuid;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
pub mod errors;
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
pub mod metadata;
|
pub mod metadata;
|
||||||
pub mod tx_processor;
|
pub mod tx_processor;
|
||||||
pub mod errors;
|
|
||||||
pub mod syncer;
|
pub mod syncer;
|
||||||
pub mod tx_mapper;
|
pub mod tx_mapper;
|
||||||
pub use syncer::Syncer;
|
pub use syncer::Syncer;
|
||||||
pub use errors::{
|
pub use errors::{
|
||||||
Error,
|
TolstoyError,
|
||||||
ErrorKind,
|
|
||||||
Result,
|
Result,
|
||||||
ResultExt,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,7 +15,7 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use schema;
|
use schema;
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
TolstoyError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ impl HeadTrackable for SyncMetadataClient {
|
||||||
let updated = tx.execute("UPDATE tolstoy_metadata SET value = ? WHERE key = ?",
|
let updated = tx.execute("UPDATE tolstoy_metadata SET value = ? WHERE key = ?",
|
||||||
&[&uuid_bytes, &schema::REMOTE_HEAD_KEY])?;
|
&[&uuid_bytes, &schema::REMOTE_HEAD_KEY])?;
|
||||||
if updated != 1 {
|
if updated != 1 {
|
||||||
bail!(ErrorKind::DuplicateMetadata(schema::REMOTE_HEAD_KEY.into()));
|
bail!(TolstoyError::DuplicateMetadata(schema::REMOTE_HEAD_KEY.into()));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ use metadata::HeadTrackable;
|
||||||
use schema::ensure_current_version;
|
use schema::ensure_current_version;
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
TolstoyError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ impl Syncer {
|
||||||
let mut uploader = UploadingTxReceiver::new(remote_client, remote_head);
|
let mut uploader = UploadingTxReceiver::new(remote_client, remote_head);
|
||||||
Processor::process(db_tx, from_tx, &mut uploader)?;
|
Processor::process(db_tx, from_tx, &mut uploader)?;
|
||||||
if !uploader.is_done {
|
if !uploader.is_done {
|
||||||
bail!(ErrorKind::TxProcessorUnfinished);
|
bail!(TolstoyError::TxProcessorUnfinished);
|
||||||
}
|
}
|
||||||
// Last tx uuid uploaded by the tx receiver.
|
// Last tx uuid uploaded by the tx receiver.
|
||||||
// It's going to be our new head.
|
// It's going to be our new head.
|
||||||
|
@ -222,7 +222,7 @@ impl Syncer {
|
||||||
// without walking the table at all, and use the tx index.
|
// without walking the table at all, and use the tx index.
|
||||||
Processor::process(&db_tx, None, &mut inquiring_tx_receiver)?;
|
Processor::process(&db_tx, None, &mut inquiring_tx_receiver)?;
|
||||||
if !inquiring_tx_receiver.is_done {
|
if !inquiring_tx_receiver.is_done {
|
||||||
bail!(ErrorKind::TxProcessorUnfinished);
|
bail!(TolstoyError::TxProcessorUnfinished);
|
||||||
}
|
}
|
||||||
let have_local_changes = match inquiring_tx_receiver.last_tx {
|
let have_local_changes = match inquiring_tx_receiver.last_tx {
|
||||||
Some(tx) => {
|
Some(tx) => {
|
||||||
|
@ -257,7 +257,7 @@ impl Syncer {
|
||||||
Syncer::upload_ours(&mut db_tx, Some(upload_from_tx), &remote_client, &remote_head)?;
|
Syncer::upload_ours(&mut db_tx, Some(upload_from_tx), &remote_client, &remote_head)?;
|
||||||
} else {
|
} else {
|
||||||
d(&format!("Unable to fast-forward the server; missing local tx mapping"));
|
d(&format!("Unable to fast-forward the server; missing local tx mapping"));
|
||||||
bail!(ErrorKind::TxIncorrectlyMapped(0));
|
bail!(TolstoyError::TxIncorrectlyMapped(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We diverged from the server.
|
// We diverged from the server.
|
||||||
|
@ -265,7 +265,7 @@ impl Syncer {
|
||||||
} else {
|
} else {
|
||||||
d(&format!("server changed since last sync."));
|
d(&format!("server changed since last sync."));
|
||||||
|
|
||||||
bail!(ErrorKind::NotYetImplemented(
|
bail!(TolstoyError::NotYetImplemented(
|
||||||
format!("Can't yet sync against changed server. Local head {:?}, remote head {:?}", locally_known_remote_head, remote_head)
|
format!("Can't yet sync against changed server. Local head {:?}, remote head {:?}", locally_known_remote_head, remote_head)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use uuid::Uuid;
|
||||||
use mentat_core::Entid;
|
use mentat_core::Entid;
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
ErrorKind,
|
TolstoyError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ impl TxMapper {
|
||||||
if txs.len() == 0 {
|
if txs.len() == 0 {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
} else if txs.len() > 1 {
|
} else if txs.len() > 1 {
|
||||||
bail!(ErrorKind::TxIncorrectlyMapped(txs.len()));
|
bail!(TolstoyError::TxIncorrectlyMapped(txs.len()));
|
||||||
}
|
}
|
||||||
Ok(Some(txs.remove(0)?))
|
Ok(Some(txs.remove(0)?))
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ impl TxMapper {
|
||||||
if uuids.len() == 0 {
|
if uuids.len() == 0 {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
} else if uuids.len() > 1 {
|
} else if uuids.len() > 1 {
|
||||||
bail!(ErrorKind::TxIncorrectlyMapped(uuids.len()));
|
bail!(TolstoyError::TxIncorrectlyMapped(uuids.len()));
|
||||||
}
|
}
|
||||||
Ok(Some(uuids.remove(0)?))
|
Ok(Some(uuids.remove(0)?))
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ test = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
combine = "2.2.2"
|
combine = "2.2.2"
|
||||||
env_logger = "0.5"
|
env_logger = "0.5"
|
||||||
|
failure = "0.1.1"
|
||||||
|
failure_derive = "0.1.1"
|
||||||
getopts = "0.2"
|
getopts = "0.2"
|
||||||
lazy_static = "0.2"
|
lazy_static = "0.2"
|
||||||
linefeed = "0.4"
|
linefeed = "0.4"
|
||||||
|
@ -28,7 +30,6 @@ tabwriter = "1"
|
||||||
tempfile = "1.1"
|
tempfile = "1.1"
|
||||||
termion = "1"
|
termion = "1"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
|
|
||||||
|
|
||||||
[dependencies.rusqlite]
|
[dependencies.rusqlite]
|
||||||
version = "0.13"
|
version = "0.13"
|
||||||
|
|
|
@ -30,14 +30,26 @@ use combine::combinator::{
|
||||||
try,
|
try,
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors as cli;
|
use CliError;
|
||||||
|
|
||||||
use edn;
|
use edn;
|
||||||
|
|
||||||
|
use failure::{
|
||||||
|
Compat,
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
use mentat::{
|
use mentat::{
|
||||||
CacheDirection,
|
CacheDirection,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bail {
|
||||||
|
($e:expr) => (
|
||||||
|
return Err($e.into());
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub static COMMAND_CACHE: &'static str = &"cache";
|
pub static COMMAND_CACHE: &'static str = &"cache";
|
||||||
pub static COMMAND_CLOSE: &'static str = &"close";
|
pub static COMMAND_CLOSE: &'static str = &"close";
|
||||||
pub static COMMAND_EXIT_LONG: &'static str = &"exit";
|
pub static COMMAND_EXIT_LONG: &'static str = &"exit";
|
||||||
|
@ -188,7 +200,7 @@ impl Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn command(s: &str) -> Result<Command, cli::Error> {
|
pub fn command(s: &str) -> Result<Command, Error> {
|
||||||
let path = || many1::<String, _>(satisfy(|c: char| !c.is_whitespace()));
|
let path = || many1::<String, _>(satisfy(|c: char| !c.is_whitespace()));
|
||||||
let argument = || many1::<String, _>(satisfy(|c: char| !c.is_whitespace()));
|
let argument = || many1::<String, _>(satisfy(|c: char| !c.is_whitespace()));
|
||||||
let arguments = || sep_end_by::<Vec<_>, _, _>(many1(satisfy(|c: char| !c.is_whitespace())), many1::<Vec<_>, _>(space())).expected("arguments");
|
let arguments = || sep_end_by::<Vec<_>, _, _>(many1(satisfy(|c: char| !c.is_whitespace())), many1::<Vec<_>, _>(space())).expected("arguments");
|
||||||
|
@ -202,7 +214,7 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
|
||||||
let edn_arg_parser = || spaces()
|
let edn_arg_parser = || spaces()
|
||||||
.with(look_ahead(string("[").or(string("{")))
|
.with(look_ahead(string("[").or(string("{")))
|
||||||
.with(many1::<Vec<_>, _>(try(any())))
|
.with(many1::<Vec<_>, _>(try(any())))
|
||||||
.and_then(|args| -> Result<String, cli::Error> {
|
.and_then(|args| -> Result<String, Compat<Error>> {
|
||||||
Ok(args.iter().collect())
|
Ok(args.iter().collect())
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -217,10 +229,10 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
|
||||||
.with(arguments())
|
.with(arguments())
|
||||||
.map(move |args| {
|
.map(move |args| {
|
||||||
if args.len() < num_args {
|
if args.len() < num_args {
|
||||||
bail!(cli::ErrorKind::CommandParse("Missing required argument".to_string()));
|
bail!(CliError::CommandParse("Missing required argument".to_string()));
|
||||||
}
|
}
|
||||||
if args.len() > num_args {
|
if args.len() > num_args {
|
||||||
bail!(cli::ErrorKind::CommandParse(format!("Unrecognized argument {:?}", args[num_args])));
|
bail!(CliError::CommandParse(format!("Unrecognized argument {:?}", args[num_args])));
|
||||||
}
|
}
|
||||||
Ok(args)
|
Ok(args)
|
||||||
})
|
})
|
||||||
|
@ -239,7 +251,7 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
|
||||||
.with(no_arg_parser())
|
.with(no_arg_parser())
|
||||||
.map(|args| {
|
.map(|args| {
|
||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
bail!(cli::ErrorKind::CommandParse(format!("Unrecognized argument {:?}", args[0])) );
|
bail!(CliError::CommandParse(format!("Unrecognized argument {:?}", args[0])) );
|
||||||
}
|
}
|
||||||
Ok(Command::Close)
|
Ok(Command::Close)
|
||||||
});
|
});
|
||||||
|
@ -248,7 +260,7 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
|
||||||
.with(no_arg_parser())
|
.with(no_arg_parser())
|
||||||
.map(|args| {
|
.map(|args| {
|
||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
bail!(cli::ErrorKind::CommandParse(format!("Unrecognized argument {:?}", args[0])) );
|
bail!(CliError::CommandParse(format!("Unrecognized argument {:?}", args[0])) );
|
||||||
}
|
}
|
||||||
Ok(Command::Exit)
|
Ok(Command::Exit)
|
||||||
});
|
});
|
||||||
|
@ -302,7 +314,7 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
|
||||||
.with(no_arg_parser())
|
.with(no_arg_parser())
|
||||||
.map(|args| {
|
.map(|args| {
|
||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
bail!(cli::ErrorKind::CommandParse(format!("Unrecognized argument {:?}", args[0])) );
|
bail!(CliError::CommandParse(format!("Unrecognized argument {:?}", args[0])) );
|
||||||
}
|
}
|
||||||
Ok(Command::Schema)
|
Ok(Command::Schema)
|
||||||
});
|
});
|
||||||
|
@ -312,10 +324,10 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
|
||||||
.with(arguments())
|
.with(arguments())
|
||||||
.map(|args| {
|
.map(|args| {
|
||||||
if args.len() < 1 {
|
if args.len() < 1 {
|
||||||
bail!(cli::ErrorKind::CommandParse("Missing required argument".to_string()));
|
bail!(CliError::CommandParse("Missing required argument".to_string()));
|
||||||
}
|
}
|
||||||
if args.len() > 2 {
|
if args.len() > 2 {
|
||||||
bail!(cli::ErrorKind::CommandParse(format!("Unrecognized argument {:?}", args[2])));
|
bail!(CliError::CommandParse(format!("Unrecognized argument {:?}", args[2])));
|
||||||
}
|
}
|
||||||
Ok(Command::Sync(args.clone()))
|
Ok(Command::Sync(args.clone()))
|
||||||
});
|
});
|
||||||
|
@ -335,7 +347,7 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
|
||||||
|
|
||||||
spaces()
|
spaces()
|
||||||
.skip(token('.'))
|
.skip(token('.'))
|
||||||
.with(choice::<[&mut Parser<Input = _, Output = Result<Command, cli::Error>>; 16], _>
|
.with(choice::<[&mut Parser<Input = _, Output = Result<Command, Error>>; 16], _>
|
||||||
([&mut try(help_parser),
|
([&mut try(help_parser),
|
||||||
&mut try(import_parser),
|
&mut try(import_parser),
|
||||||
&mut try(timer_parser),
|
&mut try(timer_parser),
|
||||||
|
@ -353,7 +365,7 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
|
||||||
&mut try(sync_parser),
|
&mut try(sync_parser),
|
||||||
&mut try(transact_parser)]))
|
&mut try(transact_parser)]))
|
||||||
.parse(s)
|
.parse(s)
|
||||||
.unwrap_or((Err(cli::ErrorKind::CommandParse(format!("Invalid command {:?}", s)).into()), "")).0
|
.unwrap_or((Err(CliError::CommandParse(format!("Invalid command {:?}", s)).into()), "")).0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
// Copyright 2016 Mozilla
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
||||||
// this file except in compliance with the License. You may obtain a copy of the
|
|
||||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
// Unless required by applicable law or agreed to in writing, software distributed
|
|
||||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
||||||
// specific language governing permissions and limitations under the License.
|
|
||||||
|
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use rusqlite;
|
|
||||||
|
|
||||||
use mentat::errors as mentat;
|
|
||||||
|
|
||||||
error_chain! {
|
|
||||||
types {
|
|
||||||
Error, ErrorKind, ResultExt, Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreign_links {
|
|
||||||
Rusqlite(rusqlite::Error);
|
|
||||||
IoError(::std::io::Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
links {
|
|
||||||
MentatError(mentat::Error, mentat::ErrorKind);
|
|
||||||
}
|
|
||||||
|
|
||||||
errors {
|
|
||||||
CommandParse(message: String) {
|
|
||||||
description("An error occured parsing the entered command")
|
|
||||||
display("{}", message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -28,7 +28,7 @@ use command_parser::{
|
||||||
command,
|
command,
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors as cli;
|
use failure::Error;
|
||||||
|
|
||||||
/// Starting prompt
|
/// Starting prompt
|
||||||
const DEFAULT_PROMPT: &'static str = "mentat=> ";
|
const DEFAULT_PROMPT: &'static str = "mentat=> ";
|
||||||
|
@ -97,7 +97,7 @@ impl InputReader {
|
||||||
/// Reads a single command, item, or statement from `stdin`.
|
/// Reads a single command, item, or statement from `stdin`.
|
||||||
/// Returns `More` if further input is required for a complete result.
|
/// Returns `More` if further input is required for a complete result.
|
||||||
/// In this case, the input received so far is buffered internally.
|
/// In this case, the input received so far is buffered internally.
|
||||||
pub fn read_input(&mut self) -> Result<InputResult, cli::Error> {
|
pub fn read_input(&mut self) -> Result<InputResult, Error> {
|
||||||
let prompt = if self.in_process_cmd.is_some() { MORE_PROMPT } else { DEFAULT_PROMPT };
|
let prompt = if self.in_process_cmd.is_some() { MORE_PROMPT } else { DEFAULT_PROMPT };
|
||||||
let prompt = format!("{blue}{prompt}{reset}",
|
let prompt = format!("{blue}{prompt}{reset}",
|
||||||
blue = color::Fg(::BLUE),
|
blue = color::Fg(::BLUE),
|
||||||
|
|
|
@ -10,12 +10,13 @@
|
||||||
|
|
||||||
#![crate_name = "mentat_cli"]
|
#![crate_name = "mentat_cli"]
|
||||||
|
|
||||||
|
#[macro_use] extern crate failure_derive;
|
||||||
#[macro_use] extern crate log;
|
#[macro_use] extern crate log;
|
||||||
#[macro_use] extern crate lazy_static;
|
#[macro_use] extern crate lazy_static;
|
||||||
#[macro_use] extern crate error_chain;
|
|
||||||
|
|
||||||
extern crate combine;
|
extern crate combine;
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
|
extern crate failure;
|
||||||
extern crate getopts;
|
extern crate getopts;
|
||||||
extern crate linefeed;
|
extern crate linefeed;
|
||||||
extern crate rusqlite;
|
extern crate rusqlite;
|
||||||
|
@ -41,7 +42,12 @@ static GREEN: color::Rgb = color::Rgb(0x77, 0xFF, 0x99);
|
||||||
pub mod command_parser;
|
pub mod command_parser;
|
||||||
pub mod input;
|
pub mod input;
|
||||||
pub mod repl;
|
pub mod repl;
|
||||||
pub mod errors;
|
|
||||||
|
#[derive(Debug, Fail)]
|
||||||
|
pub enum CliError {
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
CommandParse(String),
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run() -> i32 {
|
pub fn run() -> i32 {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
|
use failure::{
|
||||||
|
err_msg,
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
use tabwriter::TabWriter;
|
use tabwriter::TabWriter;
|
||||||
|
|
||||||
use termion::{
|
use termion::{
|
||||||
|
@ -383,7 +388,7 @@ impl Repl {
|
||||||
if self.path.is_empty() || path != self.path {
|
if self.path.is_empty() || path != self.path {
|
||||||
let next = match encryption_key {
|
let next = match encryption_key {
|
||||||
#[cfg(not(feature = "sqlcipher"))]
|
#[cfg(not(feature = "sqlcipher"))]
|
||||||
Some(_) => bail!(".open_encrypted and .empty_encrypted require the sqlcipher Mentat feature"),
|
Some(_) => return Err(err_msg(".open_encrypted and .empty_encrypted require the sqlcipher Mentat feature")),
|
||||||
#[cfg(feature = "sqlcipher")]
|
#[cfg(feature = "sqlcipher")]
|
||||||
Some(k) => {
|
Some(k) => {
|
||||||
if empty {
|
if empty {
|
||||||
|
@ -467,7 +472,7 @@ impl Repl {
|
||||||
output.flush().unwrap();
|
output.flush().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_results(&self, query_output: QueryOutput) -> Result<(), ::errors::Error> {
|
fn print_results(&self, query_output: QueryOutput) -> Result<(), Error> {
|
||||||
let stdout = ::std::io::stdout();
|
let stdout = ::std::io::stdout();
|
||||||
let mut output = TabWriter::new(stdout.lock());
|
let mut output = TabWriter::new(stdout.lock());
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue