Use concrete Mentat error types rather than failure::Error
. (#769) r=grisha
In the language of
868273409c/book/src/error-errorkind.md
Mentat is a mid-level API, not an application, and therefore we should
prefer our own error types. Writing an initial consumer of Mentat (a
Rust logins API targeting Mozilla Lockbox), I have found this to be
true: my consumer wants to consume concrete Mentat error types.
This doesn't go "all the way" and convert all sub-crates to the
Error/ErrorKind, but it does go part of the way.
This commit is contained in:
commit
5fe4f12d32
35 changed files with 447 additions and 405 deletions
|
@ -10,8 +10,6 @@
|
||||||
|
|
||||||
/// Cache traits.
|
/// Cache traits.
|
||||||
|
|
||||||
use failure;
|
|
||||||
|
|
||||||
use std::collections::{
|
use std::collections::{
|
||||||
BTreeSet,
|
BTreeSet,
|
||||||
};
|
};
|
||||||
|
@ -35,7 +33,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<Error=failure::Error> {
|
pub trait UpdateableCache<E> {
|
||||||
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<(), E>
|
||||||
where I: Iterator<Item=(Entid, Entid, TypedValue)>;
|
where I: Iterator<Item=(Entid, Entid, TypedValue)>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
use edn;
|
use edn;
|
||||||
use errors::{
|
use errors::{
|
||||||
DbError,
|
DbErrorKind,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
use edn::types::Value;
|
use edn::types::Value;
|
||||||
|
@ -159,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(|_| DbError::BadBootstrapDefinition("Unable to parse V1_SYMBOLIC_SCHEMA".into()))
|
.map_err(|_| DbErrorKind::BadBootstrapDefinition("Unable to parse V1_SYMBOLIC_SCHEMA".into()))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -210,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!(DbError::BadBootstrapDefinition(format!("Expected namespaced keyword for ident but got '{:?}'", ident))),
|
_ => bail!(DbErrorKind::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!(DbError::BadBootstrapDefinition(format!("Expected namespaced keyword for attr but got '{:?}'", attr))),
|
_ => bail!(DbErrorKind::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
|
||||||
|
@ -232,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(DbError::UnrecognizedIdent(k.to_string()))?
|
.ok_or(DbErrorKind::UnrecognizedIdent(k.to_string()))?
|
||||||
},
|
},
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
_ => bail!(DbError::BadBootstrapDefinition(format!("Expected Mentat typed value for value but got '{:?}'", value)))
|
_ => bail!(DbErrorKind::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!(DbError::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into()))
|
_ => bail!(DbErrorKind::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => bail!(DbError::BadBootstrapDefinition("Expected {...}".into()))
|
_ => bail!(DbErrorKind::BadBootstrapDefinition("Expected {...}".into()))
|
||||||
}
|
}
|
||||||
Ok(triples)
|
Ok(triples)
|
||||||
}
|
}
|
||||||
|
@ -266,11 +266,11 @@ fn symbolic_schema_to_assertions(symbolic_schema: &Value) -> Result<Vec<Value>>
|
||||||
value.clone()]));
|
value.clone()]));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => bail!(DbError::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into()))
|
_ => bail!(DbErrorKind::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => bail!(DbError::BadBootstrapDefinition("Expected {...}".into()))
|
_ => bail!(DbErrorKind::BadBootstrapDefinition("Expected {...}".into()))
|
||||||
}
|
}
|
||||||
Ok(assertions)
|
Ok(assertions)
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,10 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
|
use failure::{
|
||||||
|
ResultExt,
|
||||||
|
};
|
||||||
|
|
||||||
use num;
|
use num;
|
||||||
|
|
||||||
use rusqlite;
|
use rusqlite;
|
||||||
|
@ -107,6 +111,7 @@ use db::{
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
DbError,
|
DbError,
|
||||||
|
DbErrorKind,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -895,7 +900,7 @@ impl AttributeCaches {
|
||||||
let table = if is_fulltext { "fulltext_datoms" } else { "datoms" };
|
let table = if is_fulltext { "fulltext_datoms" } else { "datoms" };
|
||||||
let sql = format!("SELECT a, e, v, value_type_tag FROM {} WHERE a = ? ORDER BY a ASC, e ASC", table);
|
let sql = format!("SELECT a, e, v, value_type_tag FROM {} WHERE a = ? ORDER BY a ASC, e ASC", table);
|
||||||
let args: Vec<&rusqlite::types::ToSql> = vec![&attribute];
|
let args: Vec<&rusqlite::types::ToSql> = vec![&attribute];
|
||||||
let mut stmt = sqlite.prepare(&sql)?;
|
let mut stmt = sqlite.prepare(&sql).context(DbErrorKind::CacheUpdateFailed)?;
|
||||||
let replacing = true;
|
let replacing = true;
|
||||||
self.repopulate_from_aevt(schema, &mut stmt, args, replacing)
|
self.repopulate_from_aevt(schema, &mut stmt, args, replacing)
|
||||||
}
|
}
|
||||||
|
@ -1154,7 +1159,7 @@ impl CachedAttributes for AttributeCaches {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateableCache for AttributeCaches {
|
impl UpdateableCache<DbError> for AttributeCaches {
|
||||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
||||||
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)
|
||||||
|
@ -1236,7 +1241,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(|| DbError::UnknownAttribute(a))?;
|
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbErrorKind::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)
|
||||||
|
@ -1247,7 +1252,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(|| DbError::UnknownAttribute(a))?;
|
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbErrorKind::UnknownAttribute(a))?;
|
||||||
|
|
||||||
let caches = self.make_mut();
|
let caches = self.make_mut();
|
||||||
caches.reverse_cached_attributes.insert(a);
|
caches.reverse_cached_attributes.insert(a);
|
||||||
|
@ -1276,7 +1281,7 @@ impl SQLiteAttributeCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateableCache for SQLiteAttributeCache {
|
impl UpdateableCache<DbError> for SQLiteAttributeCache {
|
||||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
||||||
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)
|
||||||
|
@ -1354,7 +1359,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(|| DbError::UnknownAttribute(a))?;
|
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbErrorKind::UnknownAttribute(a))?;
|
||||||
|
|
||||||
if self.is_attribute_cached_forward(a) {
|
if self.is_attribute_cached_forward(a) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -1370,7 +1375,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(|| DbError::UnknownAttribute(a))?;
|
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbErrorKind::UnknownAttribute(a))?;
|
||||||
|
|
||||||
if self.is_attribute_cached_reverse(a) {
|
if self.is_attribute_cached_reverse(a) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -1386,7 +1391,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(|| DbError::UnknownAttribute(a))?;
|
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbErrorKind::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);
|
||||||
|
@ -1424,7 +1429,7 @@ impl InProgressSQLiteAttributeCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateableCache for InProgressSQLiteAttributeCache {
|
impl UpdateableCache<DbError> for InProgressSQLiteAttributeCache {
|
||||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
||||||
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)
|
||||||
|
|
61
db/src/db.rs
61
db/src/db.rs
|
@ -10,7 +10,9 @@
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use failure::ResultExt;
|
use failure::{
|
||||||
|
ResultExt,
|
||||||
|
};
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -56,9 +58,8 @@ use mentat_core::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
DbError,
|
DbErrorKind,
|
||||||
Result,
|
Result,
|
||||||
DbSqlErrorKind,
|
|
||||||
};
|
};
|
||||||
use metadata;
|
use metadata;
|
||||||
use schema::{
|
use schema::{
|
||||||
|
@ -257,7 +258,7 @@ 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), &[])
|
||||||
.context(DbSqlErrorKind::CouldNotSetVersionPragma)?;
|
.context(DbErrorKind::CouldNotSetVersionPragma)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,7 +269,7 @@ fn set_user_version(conn: &rusqlite::Connection, version: i32) -> Result<()> {
|
||||||
fn get_user_version(conn: &rusqlite::Connection) -> Result<i32> {
|
fn get_user_version(conn: &rusqlite::Connection) -> Result<i32> {
|
||||||
let v = 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)?;
|
}).context(DbErrorKind::CouldNotGetVersionPragma)?;
|
||||||
Ok(v)
|
Ok(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +310,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 {
|
||||||
bail!(DbError::NotYetImplemented(format!("Initial bootstrap transaction did not produce expected bootstrap schema")));
|
bail!(DbErrorKind::NotYetImplemented(format!("Initial bootstrap transaction did not produce expected bootstrap schema")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +332,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!(DbError::NotYetImplemented(format!("Opening databases with Mentat version: {}", v))),
|
v => bail!(DbErrorKind::NotYetImplemented(format!("Opening databases with Mentat version: {}", v))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,7 +362,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!(DbError::BadSQLValuePair(rusqlite::types::Value::Blob(x),
|
bail!(DbErrorKind::BadSQLValuePair(rusqlite::types::Value::Blob(x),
|
||||||
value_type_tag));
|
value_type_tag));
|
||||||
}
|
}
|
||||||
Ok(TypedValue::Uuid(u.unwrap()))
|
Ok(TypedValue::Uuid(u.unwrap()))
|
||||||
|
@ -369,7 +370,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!(DbError::BadSQLValuePair(value, value_type_tag)),
|
(_, value) => bail!(DbErrorKind::BadSQLValuePair(value, value_type_tag)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,12 +453,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!(DbError::NotYetImplemented(format!("bad idents materialized view: expected :db/ident but got {}", a)));
|
bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("bad idents materialized view: expected [entid :db/ident keyword] but got [entid :db/ident {:?}]", typed_value)));
|
bail!(DbErrorKind::NotYetImplemented(format!("bad idents materialized view: expected [entid :db/ident keyword] but got [entid :db/ident {:?}]", typed_value)));
|
||||||
}
|
}
|
||||||
}).collect()
|
}).collect()
|
||||||
}
|
}
|
||||||
|
@ -551,7 +552,7 @@ 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(&[]).context(DbSqlErrorKind::CouldNotSearch)?;
|
stmt.execute(&[]).context(DbErrorKind::CouldNotSearch)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,7 +575,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]).context(DbSqlErrorKind::TxInsertFailedToAddMissingDatoms)?;
|
stmt.execute(&[&tx]).context(DbErrorKind::TxInsertFailedToAddMissingDatoms)?;
|
||||||
|
|
||||||
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)
|
||||||
|
@ -585,7 +586,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]).context(DbSqlErrorKind::TxInsertFailedToRetractDatoms)?;
|
stmt.execute(&[&tx]).context(DbErrorKind::TxInsertFailedToRetractDatoms)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -607,7 +608,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(&[]).context(DbSqlErrorKind::DatomsUpdateFailedToRetract)?;
|
stmt.execute(&[]).context(DbErrorKind::DatomsUpdateFailedToRetract)?;
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -630,7 +631,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]).context(DbSqlErrorKind::DatomsUpdateFailedToAdd)?;
|
stmt.execute(&[&tx]).context(DbErrorKind::DatomsUpdateFailedToAdd)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -757,7 +758,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(&[]).context(DbSqlErrorKind::FailedToCreateTempTables)?;
|
stmt.execute(&[]).context(DbErrorKind::FailedToCreateTempTables)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -820,7 +821,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(s.as_str())?;
|
let mut stmt = self.prepare_cached(s.as_str())?;
|
||||||
stmt.execute(¶ms)
|
stmt.execute(¶ms)
|
||||||
.context(DbSqlErrorKind::NonFtsInsertionIntoTempSearchTableFailed)
|
.context(DbErrorKind::NonFtsInsertionIntoTempSearchTableFailed)
|
||||||
.map_err(|e| e.into())
|
.map_err(|e| e.into())
|
||||||
.map(|_c| ())
|
.map(|_c| ())
|
||||||
}).collect::<Result<Vec<()>>>();
|
}).collect::<Result<Vec<()>>>();
|
||||||
|
@ -880,7 +881,7 @@ impl MentatStoring for rusqlite::Connection {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
bail!(DbError::WrongTypeValueForFtsAssertion);
|
bail!(DbErrorKind::WrongTypeValueForFtsAssertion);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,7 +908,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).context(DbSqlErrorKind::FtsInsertionFailed)?;
|
stmt.execute(&fts_params).context(DbErrorKind::FtsInsertionFailed)?;
|
||||||
|
|
||||||
// Second, insert searches.
|
// Second, insert searches.
|
||||||
// `params` reference computed values in `block`.
|
// `params` reference computed values in `block`.
|
||||||
|
@ -935,14 +936,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).context(DbSqlErrorKind::FtsInsertionIntoTempSearchTableFailed)
|
stmt.execute(¶ms).context(DbErrorKind::FtsInsertionIntoTempSearchTableFailed)
|
||||||
.map_err(|e| e.into())
|
.map_err(|e| e.into())
|
||||||
.map(|_c| ())
|
.map(|_c| ())
|
||||||
}).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(&[]).context(DbSqlErrorKind::FtsFailedToDropSearchIds)?;
|
stmt.execute(&[]).context(DbErrorKind::FtsFailedToDropSearchIds)?;
|
||||||
results.map(|_| ())
|
results.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -974,7 +975,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!(DbError::NotYetImplemented(format!("No more than {} partitions are supported", max_partitions)));
|
bail!(DbErrorKind::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".
|
||||||
|
@ -990,7 +991,7 @@ 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[..]).context(DbSqlErrorKind::FailedToUpdatePartitionMap)?;
|
stmt.execute(¶ms[..]).context(DbErrorKind::FailedToUpdatePartitionMap)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1050,8 +1051,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!(DbError::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.unique/value", entid))),
|
Some(attribute::Unique::Value) => bail!(DbErrorKind::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.unique/value", entid))),
|
||||||
Some(attribute::Unique::Identity) => bail!(DbError::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.unique/identity", entid))),
|
Some(attribute::Unique::Identity) => bail!(DbErrorKind::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.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1065,7 +1066,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!(DbError::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.cardinality/one", entid)));
|
bail!(DbErrorKind::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.cardinality/one", entid)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2672,13 +2673,13 @@ mod tests {
|
||||||
|
|
||||||
let report = conn.transact_simple_terms(terms, InternSet::new());
|
let report = conn.transact_simple_terms(terms, InternSet::new());
|
||||||
|
|
||||||
match report.unwrap_err().downcast() {
|
match report.err().map(|e| e.kind()) {
|
||||||
Ok(DbError::SchemaConstraintViolation(errors::SchemaConstraintViolation::TypeDisagreements { conflicting_datoms })) => {
|
Some(DbErrorKind::SchemaConstraintViolation(errors::SchemaConstraintViolation::TypeDisagreements { ref 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);
|
||||||
|
|
||||||
assert_eq!(conflicting_datoms, map);
|
assert_eq!(conflicting_datoms, &map);
|
||||||
},
|
},
|
||||||
x => panic!("expected schema constraint violation, got {:?}", x),
|
x => panic!("expected schema constraint violation, got {:?}", x),
|
||||||
}
|
}
|
||||||
|
|
103
db/src/errors.rs
103
db/src/errors.rs
|
@ -13,7 +13,6 @@
|
||||||
use failure::{
|
use failure::{
|
||||||
Backtrace,
|
Backtrace,
|
||||||
Context,
|
Context,
|
||||||
Error,
|
|
||||||
Fail,
|
Fail,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,7 +42,7 @@ macro_rules! bail {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
pub type Result<T> = ::std::result::Result<T, DbError>;
|
||||||
|
|
||||||
// TODO Error/ErrorKind pair
|
// TODO Error/ErrorKind pair
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
@ -119,7 +118,7 @@ impl ::std::fmt::Display for SchemaConstraintViolation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
|
||||||
pub enum InputError {
|
pub enum InputError {
|
||||||
/// Map notation included a bad `:db/id` value.
|
/// Map notation included a bad `:db/id` value.
|
||||||
BadDbId,
|
BadDbId,
|
||||||
|
@ -143,8 +142,53 @@ impl ::std::fmt::Display for InputError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Debug)]
|
||||||
pub enum DbError {
|
pub struct DbError {
|
||||||
|
inner: Context<DbErrorKind>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Display for DbError {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
|
::std::fmt::Display::fmt(&self.inner, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fail for DbError {
|
||||||
|
fn cause(&self) -> Option<&Fail> {
|
||||||
|
self.inner.cause()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn backtrace(&self) -> Option<&Backtrace> {
|
||||||
|
self.inner.backtrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DbError {
|
||||||
|
pub fn kind(&self) -> DbErrorKind {
|
||||||
|
self.inner.get_context().clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DbErrorKind> for DbError {
|
||||||
|
fn from(kind: DbErrorKind) -> DbError {
|
||||||
|
DbError { inner: Context::new(kind) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Context<DbErrorKind>> for DbError {
|
||||||
|
fn from(inner: Context<DbErrorKind>) -> DbError {
|
||||||
|
DbError { inner: inner }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<rusqlite::Error> for DbError {
|
||||||
|
fn from(error: rusqlite::Error) -> DbError {
|
||||||
|
DbError { inner: Context::new(DbErrorKind::RusqliteError(error.to_string())) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug, Fail)]
|
||||||
|
pub enum DbErrorKind {
|
||||||
/// 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.
|
||||||
#[fail(display = "not yet implemented: {}", _0)]
|
#[fail(display = "not yet implemented: {}", _0)]
|
||||||
|
@ -203,49 +247,11 @@ pub enum DbError {
|
||||||
|
|
||||||
#[fail(display = "Cannot transact a fulltext assertion with a typed value that is not :db/valueType :db.type/string")]
|
#[fail(display = "Cannot transact a fulltext assertion with a typed value that is not :db/valueType :db.type/string")]
|
||||||
WrongTypeValueForFtsAssertion,
|
WrongTypeValueForFtsAssertion,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
// SQL errors.
|
||||||
pub struct DbSqlError {
|
#[fail(display = "could not update a cache")]
|
||||||
inner: Context<DbSqlErrorKind>,
|
CacheUpdateFailed,
|
||||||
}
|
|
||||||
|
|
||||||
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")]
|
#[fail(display = "Could not set_user_version")]
|
||||||
CouldNotSetVersionPragma,
|
CouldNotSetVersionPragma,
|
||||||
|
|
||||||
|
@ -284,4 +290,9 @@ pub enum DbSqlErrorKind {
|
||||||
|
|
||||||
#[fail(display = "Could not update partition map")]
|
#[fail(display = "Could not update partition map")]
|
||||||
FailedToUpdatePartitionMap,
|
FailedToUpdatePartitionMap,
|
||||||
|
|
||||||
|
// It would be better to capture the underlying `rusqlite::Error`, but that type doesn't
|
||||||
|
// implement many useful traits, including `Clone`, `Eq`, and `PartialEq`.
|
||||||
|
#[fail(display = "SQL error: _0")]
|
||||||
|
RusqliteError(String),
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ use edn::{
|
||||||
|
|
||||||
use errors;
|
use errors;
|
||||||
use errors::{
|
use errors::{
|
||||||
DbError,
|
DbErrorKind,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
use schema::{
|
use schema::{
|
||||||
|
@ -69,7 +69,7 @@ impl TransactableValue for ValueAndSpan {
|
||||||
Ok(EntityPlace::Entid(entities::EntidOrIdent::Ident(v)))
|
Ok(EntityPlace::Entid(entities::EntidOrIdent::Ident(v)))
|
||||||
} else {
|
} else {
|
||||||
// We only allow namespaced idents.
|
// We only allow namespaced idents.
|
||||||
bail!(DbError::InputError(errors::InputError::BadEntityPlace))
|
bail!(DbErrorKind::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!(DbError::InputError(errors::InputError::BadEntityPlace)),
|
EntityPlace::LookupRef(_) => bail!(DbErrorKind::InputError(errors::InputError::BadEntityPlace)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => bail!(DbError::InputError(errors::InputError::BadEntityPlace)),
|
_ => bail!(DbErrorKind::InputError(errors::InputError::BadEntityPlace)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Nil |
|
Nil |
|
||||||
|
@ -102,7 +102,7 @@ impl TransactableValue for ValueAndSpan {
|
||||||
NamespacedSymbol(_) |
|
NamespacedSymbol(_) |
|
||||||
Vector(_) |
|
Vector(_) |
|
||||||
Set(_) |
|
Set(_) |
|
||||||
Map(_) => bail!(DbError::InputError(errors::InputError::BadEntityPlace)),
|
Map(_) => bail!(DbErrorKind::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!(DbError::BadValuePair(format!("{:?}", self), value_type));
|
bail!(DbErrorKind::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!(DbError::InputError(errors::InputError::BadEntityPlace)),
|
TypedValue::Uuid(_) => bail!(DbErrorKind::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(|| DbError::UnrecognizedIdent(format!("couldn't lookup [a v]: {:?}", (*av).clone())).into()),
|
.ok_or_else(|| DbErrorKind::UnrecognizedIdent(format!("couldn't lookup [a v]: {:?}", (*av).clone())).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ use itertools::Itertools;
|
||||||
|
|
||||||
pub use errors::{
|
pub use errors::{
|
||||||
DbError,
|
DbError,
|
||||||
|
DbErrorKind,
|
||||||
Result,
|
Result,
|
||||||
SchemaConstraintViolation,
|
SchemaConstraintViolation,
|
||||||
};
|
};
|
||||||
|
@ -116,7 +117,7 @@ pub fn to_namespaced_keyword(s: &str) -> Result<symbols::Keyword> {
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
nsk.ok_or(DbError::NotYetImplemented(format!("InvalidKeyword: {}", s)).into())
|
nsk.ok_or(DbErrorKind::NotYetImplemented(format!("InvalidKeyword: {}", s)).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepare an SQL `VALUES` block, like (?, ?, ?), (?, ?, ?).
|
/// Prepare an SQL `VALUES` block, like (?, ?, ?), (?, ?, ?).
|
||||||
|
|
|
@ -35,7 +35,7 @@ use add_retract_alter_set::{
|
||||||
use edn::symbols;
|
use edn::symbols;
|
||||||
use entids;
|
use entids;
|
||||||
use errors::{
|
use errors::{
|
||||||
DbError,
|
DbErrorKind,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
|
@ -132,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!(DbError::BadSchemaAssertion(format!("Attempted to retract :db/isComponent with the wrong value {:?}.", v)));
|
bail!(DbErrorKind::BadSchemaAssertion(format!("Attempted to retract :db/isComponent with the wrong value {:?}.", v)));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -147,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!(DbError::BadSchemaAssertion(format!("Attempted to retract :db/unique with the wrong value {}.", v)));
|
bail!(DbErrorKind::BadSchemaAssertion(format!("Attempted to retract :db/unique with the wrong value {}.", v)));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [:db/retract _ :db/unique :db.unique/_] but got [:db/retract {} :db/unique {:?}]", entid, value)))
|
_ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [:db/retract _ :db/unique :db.unique/_] but got [:db/retract {} :db/unique {:?}]", entid, value)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
bail!(DbError::BadSchemaAssertion(format!("Retracting attribute {} for entity {} not permitted.", attr, entid)));
|
bail!(DbErrorKind::BadSchemaAssertion(format!("Retracting attribute {} for entity {} not permitted.", attr, entid)));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,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!(DbError::BadSchemaAssertion(format!("Expected [... :db/doc \"string value\"] but got [... :db/doc {:?}] for entid {} and attribute {}", value, entid, attr)))
|
_ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/doc \"string value\"] but got [... :db/doc {:?}] for entid {} and attribute {}", value, entid, attr)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -183,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!(DbError::BadSchemaAssertion(format!("Expected [... :db/valueType :db.type/*] but got [... :db/valueType {:?}] for entid {} and attribute {}", value, entid, attr)))
|
_ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/valueType :db.type/*] but got [... :db/valueType {:?}] for entid {} and attribute {}", value, entid, attr)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -191,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!(DbError::BadSchemaAssertion(format!("Expected [... :db/cardinality :db.cardinality/many|:db.cardinality/one] but got [... :db/cardinality {:?}]", value)))
|
_ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/cardinality :db.cardinality/many|:db.cardinality/one] but got [... :db/cardinality {:?}]", value)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -199,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!(DbError::BadSchemaAssertion(format!("Expected [... :db/unique :db.unique/value|:db.unique/identity] but got [... :db/unique {:?}]", value)))
|
_ => bail!(DbErrorKind::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!(DbError::BadSchemaAssertion(format!("Expected [... :db/index true|false] but got [... :db/index {:?}]", value)))
|
_ => bail!(DbErrorKind::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!(DbError::BadSchemaAssertion(format!("Expected [... :db/fulltext true|false] but got [... :db/fulltext {:?}]", value)))
|
_ => bail!(DbErrorKind::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!(DbError::BadSchemaAssertion(format!("Expected [... :db/isComponent true|false] but got [... :db/isComponent {:?}]", value)))
|
_ => bail!(DbErrorKind::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!(DbError::BadSchemaAssertion(format!("Expected [... :db/noHistory true|false] but got [... :db/noHistory {:?}]", value)))
|
_ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/noHistory true|false] but got [... :db/noHistory {:?}]", value)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
bail!(DbError::BadSchemaAssertion(format!("Do not recognize attribute {} for entid {}", attr, entid)))
|
bail!(DbErrorKind::BadSchemaAssertion(format!("Do not recognize attribute {} for entid {}", attr, entid)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -244,7 +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().context(DbError::BadSchemaAssertion(format!("Schema alteration for new attribute with entid {} is not valid", entid)))?;
|
builder.validate_install_attribute().context(DbErrorKind::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,7 +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().context(DbError::BadSchemaAssertion(format!("Schema alteration for existing attribute with entid {} is not valid", entid)))?;
|
builder.validate_alter_attribute().context(DbErrorKind::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);
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
use db::TypedSQLValue;
|
use db::TypedSQLValue;
|
||||||
use edn;
|
use edn;
|
||||||
use errors::{
|
use errors::{
|
||||||
DbError,
|
DbErrorKind,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
use edn::symbols;
|
use edn::symbols;
|
||||||
|
@ -42,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!(DbError::BadSchemaAssertion(format!(":db/unique :db/unique_value without :db/index true for entid: {}", ident())))
|
bail!(DbErrorKind::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!(DbError::BadSchemaAssertion(format!(":db/unique :db/unique_identity without :db/index true for entid: {}", ident())))
|
bail!(DbErrorKind::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!(DbError::BadSchemaAssertion(format!(":db/fulltext true without :db/valueType :db.type/string for entid: {}", ident())))
|
bail!(DbErrorKind::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!(DbError::BadSchemaAssertion(format!(":db/fulltext true without :db/index true for entid: {}", ident())))
|
bail!(DbErrorKind::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!(DbError::BadSchemaAssertion(format!(":db/isComponent true without :db/valueType :db.type/ref for entid: {}", ident())))
|
bail!(DbErrorKind::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
|
||||||
|
@ -153,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!(DbError::BadSchemaAssertion("Schema attribute for new attribute does not set :db/valueType".into()));
|
bail!(DbErrorKind::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!(DbError::BadSchemaAssertion("Schema alteration must not set :db/valueType".into()));
|
bail!(DbErrorKind::BadSchemaAssertion("Schema alteration must not set :db/valueType".into()));
|
||||||
}
|
}
|
||||||
if self.fulltext.is_some() {
|
if self.fulltext.is_some() {
|
||||||
bail!(DbError::BadSchemaAssertion("Schema alteration must not set :db/fulltext".into()));
|
bail!(DbErrorKind::BadSchemaAssertion("Schema alteration must not set :db/fulltext".into()));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -250,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(DbError::UnrecognizedEntid(entid).into())
|
self.get_ident(entid).ok_or(DbErrorKind::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(DbError::UnrecognizedIdent(ident.to_string()).into())
|
self.get_entid(&ident).ok_or(DbErrorKind::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(DbError::UnrecognizedEntid(entid).into())
|
self.attribute_for_entid(entid).ok_or(DbErrorKind::UnrecognizedEntid(entid).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a valid `Schema` from the constituent maps.
|
/// Create a valid `Schema` from the constituent maps.
|
||||||
|
@ -274,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(DbError::UnrecognizedIdent(symbolic_ident.to_string()))?;
|
let ident: i64 = *ident_map.get(&symbolic_ident).ok_or(DbErrorKind::UnrecognizedIdent(symbolic_ident.to_string()))?;
|
||||||
let attr: i64 = *ident_map.get(&symbolic_attr).ok_or(DbError::UnrecognizedIdent(symbolic_attr.to_string()))?;
|
let attr: i64 = *ident_map.get(&symbolic_attr).ok_or(DbErrorKind::UnrecognizedIdent(symbolic_attr.to_string()))?;
|
||||||
Ok((ident, attr, value))
|
Ok((ident, attr, value))
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
|
@ -308,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!(DbError::BadValuePair(format!("{}", value), value_type)),
|
None => bail!(DbErrorKind::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),
|
||||||
|
@ -334,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!(DbError::BadValuePair(format!("{}", value), vt)),
|
=> bail!(DbErrorKind::BadValuePair(format!("{}", value), vt)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -434,13 +434,8 @@ mod test {
|
||||||
no_history: false,
|
no_history: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err().map(|e| e.kind());
|
||||||
assert!(err.is_some());
|
assert_eq!(err, Some(DbErrorKind::BadSchemaAssertion(":db/unique :db/unique_value without :db/index true for entid: :foo/bar".into())));
|
||||||
|
|
||||||
match err.unwrap().downcast() {
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -457,13 +452,8 @@ mod test {
|
||||||
no_history: false,
|
no_history: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err().map(|e| e.kind());
|
||||||
assert!(err.is_some());
|
assert_eq!(err, Some(DbErrorKind::BadSchemaAssertion(":db/unique :db/unique_identity without :db/index true for entid: :foo/bar".into())));
|
||||||
|
|
||||||
match err.unwrap().downcast() {
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -480,13 +470,8 @@ mod test {
|
||||||
no_history: false,
|
no_history: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err().map(|e| e.kind());
|
||||||
assert!(err.is_some());
|
assert_eq!(err, Some(DbErrorKind::BadSchemaAssertion(":db/isComponent true without :db/valueType :db.type/ref for entid: :foo/bar".into())));
|
||||||
|
|
||||||
match err.unwrap().downcast() {
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -503,13 +488,8 @@ mod test {
|
||||||
no_history: false,
|
no_history: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err().map(|e| e.kind());
|
||||||
assert!(err.is_some());
|
assert_eq!(err, Some(DbErrorKind::BadSchemaAssertion(":db/fulltext true without :db/index true for entid: :foo/bar".into())));
|
||||||
|
|
||||||
match err.unwrap().downcast() {
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalid_schema_fulltext_index_not_string() {
|
fn invalid_schema_fulltext_index_not_string() {
|
||||||
|
@ -525,12 +505,7 @@ mod test {
|
||||||
no_history: false,
|
no_history: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err().map(|e| e.kind());
|
||||||
assert!(err.is_some());
|
assert_eq!(err, Some(DbErrorKind::BadSchemaAssertion(":db/fulltext true without :db/valueType :db.type/string for entid: :foo/bar".into())));
|
||||||
|
|
||||||
match err.unwrap().downcast() {
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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::{
|
||||||
DbError,
|
DbErrorKind,
|
||||||
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!(DbError::InputError(errors::InputError::BadDbId))
|
bail!(DbErrorKind::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!(DbError::SchemaConstraintViolation(errors::SchemaConstraintViolation::ConflictingUpserts { conflicting_upserts }));
|
bail!(DbErrorKind::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!(DbError::UnrecognizedEntid(e))
|
bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("Cannot resolve (lookup-ref {} {:?}) with attribute that is not :db/unique", lr_a, lr_typed_value)))
|
bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("Unknown transaction function {}", unknown))),
|
unknown @ _ => bail!(DbErrorKind::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::EntidOrIdent) -> Result<KnownEntidOr<LookupRefOrTempId>> {
|
fn entity_v_into_term_e<W: TransactableValue>(&mut self, x: entmod::ValuePlace<W>, backward_a: &entmod::EntidOrIdent) -> Result<KnownEntidOr<LookupRefOrTempId>> {
|
||||||
match backward_a.unreversed() {
|
match backward_a.unreversed() {
|
||||||
None => {
|
None => {
|
||||||
bail!(DbError::NotYetImplemented(format!("Cannot explode map notation value in :attr/_reversed notation for forward attribute")));
|
bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("Cannot use :attr/_reversed notation for attribute {} that is not :db/valueType :db.type/ref", forward_a)))
|
bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("Cannot use :attr/_reversed notation for attribute {} with value that is not :db.valueType :db.type/ref", forward_a)))
|
bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("Unknown transaction function {}", unknown))),
|
unknown @ _ => bail!(DbErrorKind::NotYetImplemented(format!("Unknown transaction function {}", unknown))),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
entmod::ValuePlace::Vector(_) =>
|
entmod::ValuePlace::Vector(_) =>
|
||||||
bail!(DbError::NotYetImplemented(format!("Cannot explode vector value in :attr/_reversed notation for attribute {}", forward_a))),
|
bail!(DbErrorKind::NotYetImplemented(format!("Cannot explode vector value in :attr/_reversed notation for attribute {}", forward_a))),
|
||||||
|
|
||||||
entmod::ValuePlace::MapNotation(_) =>
|
entmod::ValuePlace::MapNotation(_) =>
|
||||||
bail!(DbError::NotYetImplemented(format!("Cannot explode map notation value in :attr/_reversed notation for attribute {}", forward_a))),
|
bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("Cannot resolve value lookup ref for attribute {} that is not :db/valueType :db.type/ref", a)))
|
bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("Unknown transaction function {}", unknown))),
|
unknown @ _ => bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("Transaction function {} produced value of type {} but expected type {}",
|
bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("Cannot explode vector value for attribute {} that is not :db.cardinality :db.cardinality/many", a)));
|
bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("Cannot explode nested map value in :db/retract for attribute {}", a)));
|
bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("Cannot explode nested map value for attribute {} that is not :db/valueType :db.type/ref", a)))
|
bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("Cannot explode nested map value that would lead to dangling entity for attribute {}", a)));
|
bail!(DbErrorKind::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!(DbError::SchemaConstraintViolation(errors::SchemaConstraintViolation::ConflictingUpserts { conflicting_upserts }));
|
bail!(DbErrorKind::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!(DbError::SchemaConstraintViolation(errors::SchemaConstraintViolation::TypeDisagreements { conflicting_datoms: errors }));
|
bail!(DbErrorKind::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!(DbError::SchemaConstraintViolation(errors::SchemaConstraintViolation::CardinalityConflicts { conflicts: errors }));
|
bail!(DbErrorKind::SchemaConstraintViolation(errors::SchemaConstraintViolation::CardinalityConflicts { conflicts: errors }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pipeline stage 4: final terms (after rewriting) -> DB insertions.
|
// Pipeline stage 4: final terms (after rewriting) -> DB insertions.
|
||||||
|
|
|
@ -22,7 +22,7 @@ use indexmap;
|
||||||
use petgraph::unionfind;
|
use petgraph::unionfind;
|
||||||
|
|
||||||
use errors::{
|
use errors::{
|
||||||
DbError,
|
DbErrorKind,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
use types::{
|
use types::{
|
||||||
|
@ -331,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!(DbError::NotYetImplemented(format!("[:db/retract ...] entity referenced tempid that did not upsert: one of {}, {}", t1, t2))),
|
(OpType::Retract, _, _) => bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("[:db/retract ...] entity referenced tempid that did not upsert: {}", t))),
|
(OpType::Retract, _) => bail!(DbErrorKind::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!(DbError::NotYetImplemented(format!("[:db/retract ...] entity referenced tempid that did not upsert: {}", t))),
|
(OpType::Retract, _) => bail!(DbErrorKind::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.
|
||||||
|
|
|
@ -32,7 +32,6 @@ use clauses::{
|
||||||
use errors::{
|
use errors::{
|
||||||
AlgebrizerError,
|
AlgebrizerError,
|
||||||
BindingError,
|
BindingError,
|
||||||
InvalidBinding,
|
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,12 +58,12 @@ impl ConjoiningClauses {
|
||||||
|
|
||||||
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!(InvalidBinding::new(where_fn.operator.clone(), BindingError::NoBoundVariable));
|
bail!(AlgebrizerError::InvalidBinding(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!(InvalidBinding::new(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
bail!(AlgebrizerError::InvalidBinding(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should have exactly four bindings. Destructure them now.
|
// We should have exactly four bindings. Destructure them now.
|
||||||
|
@ -72,7 +71,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 > 4 {
|
if bindings_count < 1 || bindings_count > 4 {
|
||||||
bail!(InvalidBinding::new(where_fn.operator.clone(),
|
bail!(AlgebrizerError::InvalidBinding(where_fn.operator.clone(),
|
||||||
BindingError::InvalidNumberOfBindings {
|
BindingError::InvalidNumberOfBindings {
|
||||||
number: bindings.len(),
|
number: bindings.len(),
|
||||||
expected: 4,
|
expected: 4,
|
||||||
|
@ -83,7 +82,7 @@ impl ConjoiningClauses {
|
||||||
},
|
},
|
||||||
Binding::BindScalar(_) |
|
Binding::BindScalar(_) |
|
||||||
Binding::BindTuple(_) |
|
Binding::BindTuple(_) |
|
||||||
Binding::BindColl(_) => bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::ExpectedBindRel)),
|
Binding::BindColl(_) => bail!(AlgebrizerError::InvalidBinding(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();
|
||||||
|
@ -246,7 +245,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!(InvalidBinding::new(var.name(), BindingError::UnexpectedBinding));
|
bail!(AlgebrizerError::InvalidBinding(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.
|
||||||
|
|
|
@ -33,7 +33,6 @@ use clauses::convert::ValueConversion;
|
||||||
use errors::{
|
use errors::{
|
||||||
AlgebrizerError,
|
AlgebrizerError,
|
||||||
BindingError,
|
BindingError,
|
||||||
InvalidBinding,
|
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -125,12 +124,12 @@ impl ConjoiningClauses {
|
||||||
|
|
||||||
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!(InvalidBinding::new(where_fn.operator.clone(), BindingError::NoBoundVariable));
|
bail!(AlgebrizerError::InvalidBinding(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!(InvalidBinding::new(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
bail!(AlgebrizerError::InvalidBinding(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
||||||
}
|
}
|
||||||
|
|
||||||
let schema = known.schema;
|
let schema = known.schema;
|
||||||
|
|
|
@ -553,7 +553,7 @@ mod testing {
|
||||||
: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).expect_err("algebrization should have failed");
|
let err = algebrize(known, parsed).expect_err("algebrization should have failed");
|
||||||
match err.downcast().expect("expected AlgebrizerError") {
|
match err {
|
||||||
AlgebrizerError::UnboundVariable(var) => { assert_eq!(var, PlainSymbol("?x".to_string())); },
|
AlgebrizerError::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),
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ use clauses::{
|
||||||
use errors::{
|
use errors::{
|
||||||
AlgebrizerError,
|
AlgebrizerError,
|
||||||
BindingError,
|
BindingError,
|
||||||
InvalidBinding,
|
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,12 +65,12 @@ impl ConjoiningClauses {
|
||||||
|
|
||||||
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!(InvalidBinding::new(where_fn.operator.clone(), BindingError::NoBoundVariable));
|
bail!(AlgebrizerError::InvalidBinding(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!(InvalidBinding::new(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
bail!(AlgebrizerError::InvalidBinding(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should have exactly one binding. Destructure it now.
|
// We should have exactly one binding. Destructure it now.
|
||||||
|
@ -79,7 +78,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!(InvalidBinding::new(where_fn.operator.clone(),
|
bail!(AlgebrizerError::InvalidBinding(where_fn.operator.clone(),
|
||||||
BindingError::InvalidNumberOfBindings {
|
BindingError::InvalidNumberOfBindings {
|
||||||
number: bindings_count,
|
number: bindings_count,
|
||||||
expected: 1,
|
expected: 1,
|
||||||
|
@ -93,7 +92,7 @@ impl ConjoiningClauses {
|
||||||
Binding::BindColl(v) => v,
|
Binding::BindColl(v) => v,
|
||||||
Binding::BindScalar(_) |
|
Binding::BindScalar(_) |
|
||||||
Binding::BindTuple(_) => {
|
Binding::BindTuple(_) => {
|
||||||
bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::ExpectedBindRelOrBindColl))
|
bail!(AlgebrizerError::InvalidBinding(where_fn.operator.clone(), BindingError::ExpectedBindRelOrBindColl))
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -144,12 +143,12 @@ impl ConjoiningClauses {
|
||||||
|
|
||||||
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!(InvalidBinding::new(where_fn.operator.clone(), BindingError::NoBoundVariable));
|
bail!(AlgebrizerError::InvalidBinding(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!(InvalidBinding::new(where_fn.operator.clone(), BindingError::RepeatedBoundVariable));
|
bail!(AlgebrizerError::InvalidBinding(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.
|
||||||
|
@ -157,7 +156,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!(InvalidBinding::new(where_fn.operator.clone(),
|
bail!(AlgebrizerError::InvalidBinding(where_fn.operator.clone(),
|
||||||
BindingError::InvalidNumberOfBindings {
|
BindingError::InvalidNumberOfBindings {
|
||||||
number: bindings.len(),
|
number: bindings.len(),
|
||||||
expected: 5,
|
expected: 5,
|
||||||
|
@ -167,7 +166,7 @@ impl ConjoiningClauses {
|
||||||
},
|
},
|
||||||
Binding::BindScalar(_) |
|
Binding::BindScalar(_) |
|
||||||
Binding::BindTuple(_) |
|
Binding::BindTuple(_) |
|
||||||
Binding::BindColl(_) => bail!(InvalidBinding::new(where_fn.operator.clone(), BindingError::ExpectedBindRel)),
|
Binding::BindColl(_) => bail!(AlgebrizerError::InvalidBinding(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);
|
||||||
|
|
|
@ -11,17 +11,9 @@
|
||||||
extern crate mentat_query;
|
extern crate mentat_query;
|
||||||
|
|
||||||
use std; // To refer to std::result::Result.
|
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,
|
||||||
};
|
};
|
||||||
|
@ -30,7 +22,7 @@ use self::mentat_query::{
|
||||||
PlainSymbol,
|
PlainSymbol,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, AlgebrizerError>;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! bail {
|
macro_rules! bail {
|
||||||
|
@ -39,44 +31,7 @@ macro_rules! bail {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
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,
|
||||||
|
@ -96,7 +51,7 @@ pub enum BindingError {
|
||||||
InvalidNumberOfBindings { number: usize, expected: usize },
|
InvalidNumberOfBindings { number: usize, expected: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Clone, Debug, Eq, Fail, PartialEq)]
|
||||||
pub enum AlgebrizerError {
|
pub enum AlgebrizerError {
|
||||||
#[fail(display = "{} var {} is duplicated", _0, _1)]
|
#[fail(display = "{} var {} is duplicated", _0, _1)]
|
||||||
DuplicateVariableError(PlainSymbol, &'static str),
|
DuplicateVariableError(PlainSymbol, &'static str),
|
||||||
|
@ -144,4 +99,16 @@ pub enum AlgebrizerError {
|
||||||
|
|
||||||
#[fail(display = "non-matching variables in 'not' clause")]
|
#[fail(display = "non-matching variables in 'not' clause")]
|
||||||
NonMatchingVariablesInNotClause,
|
NonMatchingVariablesInNotClause,
|
||||||
|
|
||||||
|
#[fail(display = "binding error in {}: {:?}", _0, _1)]
|
||||||
|
InvalidBinding(PlainSymbol, BindingError),
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
EdnParseError(#[cause] EdnParseError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<EdnParseError> for AlgebrizerError {
|
||||||
|
fn from(error: EdnParseError) -> AlgebrizerError {
|
||||||
|
AlgebrizerError::EdnParseError(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,6 @@ pub use errors::{
|
||||||
AlgebrizerError,
|
AlgebrizerError,
|
||||||
BindingError,
|
BindingError,
|
||||||
Result,
|
Result,
|
||||||
InvalidBinding,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use clauses::{
|
pub use clauses::{
|
||||||
|
|
|
@ -33,7 +33,6 @@ use mentat_query_algebrizer::{
|
||||||
AlgebrizerError,
|
AlgebrizerError,
|
||||||
BindingError,
|
BindingError,
|
||||||
ComputedTable,
|
ComputedTable,
|
||||||
InvalidBinding,
|
|
||||||
Known,
|
Known,
|
||||||
QueryInputs,
|
QueryInputs,
|
||||||
};
|
};
|
||||||
|
@ -255,14 +254,8 @@ fn test_ground_coll_heterogeneous_types() {
|
||||||
let q = r#"[:find ?x :where [?x _ ?v] [(ground [false 8.5]) [?v ...]]]"#;
|
let q = r#"[:find ?x :where [?x _ ?v] [(ground [false 8.5]) [?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);
|
assert_eq!(bails(known, &q),
|
||||||
match e.downcast().expect("proper error") {
|
AlgebrizerError::InvalidGroundConstant);
|
||||||
AlgebrizerError::InvalidGroundConstant => {
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
panic!();
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -270,14 +263,8 @@ fn test_ground_rel_heterogeneous_types() {
|
||||||
let q = r#"[:find ?x :where [?x _ ?v] [(ground [[false] [5]]) [[?v]]]]"#;
|
let q = r#"[:find ?x :where [?x _ ?v] [(ground [[false] [5]]) [[?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);
|
assert_eq!(bails(known, &q),
|
||||||
match e.downcast().expect("proper error") {
|
AlgebrizerError::InvalidGroundConstant);
|
||||||
AlgebrizerError::InvalidGroundConstant => {
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
panic!();
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -285,15 +272,8 @@ 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: InvalidBinding = bails(known, &q).downcast().expect("proper error");
|
assert_eq!(bails(known, &q),
|
||||||
assert_eq!(e.function, PlainSymbol::plain("ground"));
|
AlgebrizerError::InvalidBinding(PlainSymbol::plain("ground"), BindingError::RepeatedBoundVariable));
|
||||||
match e.inner.get_context() {
|
|
||||||
&BindingError::RepeatedBoundVariable => {
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
panic!();
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -301,15 +281,8 @@ 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: InvalidBinding = bails(known, &q).downcast().expect("expected InvalidBinding");
|
assert_eq!(bails(known, &q),
|
||||||
assert_eq!(e.function, PlainSymbol::plain("ground"));
|
AlgebrizerError::InvalidBinding(PlainSymbol::plain("ground"), BindingError::RepeatedBoundVariable));
|
||||||
match e.inner.get_context() {
|
|
||||||
&BindingError::RepeatedBoundVariable => {
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
panic!();
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -317,15 +290,8 @@ 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).downcast().expect("proper error");
|
assert_eq!(bails(known, &q),
|
||||||
match e {
|
AlgebrizerError::UnboundVariable(PlainSymbol::plain("?v")));
|
||||||
AlgebrizerError::UnboundVariable(PlainSymbol(v)) => {
|
|
||||||
assert_eq!(v, "?v".to_string());
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
panic!();
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -341,13 +307,6 @@ 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).downcast().expect("proper error");
|
assert_eq!(bails_with_inputs(known, &q, i),
|
||||||
match e {
|
AlgebrizerError::UnboundVariable(PlainSymbol::plain("?x")));
|
||||||
AlgebrizerError::UnboundVariable(v) => {
|
|
||||||
assert_eq!(v.0, "?x");
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
panic!();
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,27 +78,21 @@ 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).downcast().expect("proper cause") {
|
assert_eq!(bails(known, query),
|
||||||
AlgebrizerError::InvalidArgumentType(op, why, idx) => {
|
AlgebrizerError::InvalidArgumentType(
|
||||||
assert_eq!(op, PlainSymbol::plain(">"));
|
PlainSymbol::plain(">"),
|
||||||
assert_eq!(why, ValueTypeSet::of_numeric_and_instant_types());
|
ValueTypeSet::of_numeric_and_instant_types(),
|
||||||
assert_eq!(idx, 1);
|
1));
|
||||||
},
|
|
||||||
_ => panic!("Expected InvalidArgument."),
|
|
||||||
}
|
|
||||||
|
|
||||||
let query = r#"[:find ?e
|
let query = r#"[:find ?e
|
||||||
: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).downcast().expect("proper cause") {
|
assert_eq!(bails(known, query),
|
||||||
AlgebrizerError::InvalidArgumentType(op, why, idx) => {
|
AlgebrizerError::InvalidArgumentType(
|
||||||
assert_eq!(op, PlainSymbol::plain(">"));
|
PlainSymbol::plain(">"),
|
||||||
assert_eq!(why, ValueTypeSet::of_numeric_and_instant_types());
|
ValueTypeSet::of_numeric_and_instant_types(),
|
||||||
assert_eq!(idx, 0); // We get this right.
|
0)); // We get this right.
|
||||||
},
|
|
||||||
_ => panic!("Expected InvalidArgument."),
|
|
||||||
}
|
|
||||||
|
|
||||||
// You can try using a number, which is valid input to a numeric predicate.
|
// You can try using a number, which is valid input to a numeric predicate.
|
||||||
// In this store and query, though, that means we expect `?t` to be both
|
// In this store and query, though, that means we expect `?t` to be both
|
||||||
|
|
|
@ -13,9 +13,6 @@
|
||||||
// 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,
|
||||||
|
@ -28,6 +25,7 @@ use mentat_query::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use mentat_query_algebrizer::{
|
use mentat_query_algebrizer::{
|
||||||
|
AlgebrizerError,
|
||||||
ConjoiningClauses,
|
ConjoiningClauses,
|
||||||
Known,
|
Known,
|
||||||
QueryInputs,
|
QueryInputs,
|
||||||
|
@ -83,12 +81,12 @@ impl SchemaBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bails(known: Known, input: &str) -> Error {
|
pub fn bails(known: Known, input: &str) -> AlgebrizerError {
|
||||||
let parsed = parse_find_string(input).expect("query input to have parsed");
|
let parsed = parse_find_string(input).expect("query input to have parsed");
|
||||||
algebrize(known, parsed).expect_err("algebrize to have failed")
|
algebrize(known, parsed).expect_err("algebrize to have failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bails_with_inputs(known: Known, input: &str, inputs: QueryInputs) -> Error {
|
pub fn bails_with_inputs(known: Known, input: &str, inputs: QueryInputs) -> AlgebrizerError {
|
||||||
let parsed = parse_find_string(input).expect("query input to have parsed");
|
let parsed = parse_find_string(input).expect("query input to have parsed");
|
||||||
algebrize_with_inputs(known, parsed, 0, inputs).expect_err("algebrize to have failed")
|
algebrize_with_inputs(known, parsed, 0, inputs).expect_err("algebrize to have failed")
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,17 +10,16 @@
|
||||||
|
|
||||||
use std; // To refer to std::result::Result.
|
use std; // To refer to std::result::Result.
|
||||||
|
|
||||||
use failure::{
|
use rusqlite;
|
||||||
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,
|
||||||
|
@ -33,7 +32,7 @@ macro_rules! bail {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, ProjectorError>;
|
||||||
|
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Debug, Fail)]
|
||||||
pub enum ProjectorError {
|
pub enum ProjectorError {
|
||||||
|
@ -62,4 +61,33 @@ pub enum ProjectorError {
|
||||||
|
|
||||||
#[fail(display = "min/max expressions: {} (max 1), corresponding: {}", _0, _1)]
|
#[fail(display = "min/max expressions: {} (max 1), corresponding: {}", _0, _1)]
|
||||||
AmbiguousAggregates(usize, usize),
|
AmbiguousAggregates(usize, usize),
|
||||||
|
|
||||||
|
// It would be better to capture the underlying `rusqlite::Error`, but that type doesn't
|
||||||
|
// implement many useful traits, including `Clone`, `Eq`, and `PartialEq`.
|
||||||
|
#[fail(display = "SQL error: _0")]
|
||||||
|
RusqliteError(String),
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
DbError(#[cause] mentat_db::DbError),
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
PullError(#[cause] mentat_query_pull::PullError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<rusqlite::Error> for ProjectorError {
|
||||||
|
fn from(error: rusqlite::Error) -> ProjectorError {
|
||||||
|
ProjectorError::RusqliteError(error.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mentat_db::DbError> for ProjectorError {
|
||||||
|
fn from(error: mentat_db::DbError) -> ProjectorError {
|
||||||
|
ProjectorError::DbError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mentat_query_pull::PullError> for ProjectorError {
|
||||||
|
fn from(error: mentat_query_pull::PullError) -> ProjectorError {
|
||||||
|
ProjectorError::PullError(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,7 @@ pub use relresult::{
|
||||||
StructuredRelResult,
|
StructuredRelResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
use errors::{
|
pub use errors::{
|
||||||
ProjectorError,
|
ProjectorError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
|
@ -101,7 +101,7 @@ fn test_the_without_max_or_min() {
|
||||||
use ::mentat_query_projector::errors::{
|
use ::mentat_query_projector::errors::{
|
||||||
ProjectorError,
|
ProjectorError,
|
||||||
};
|
};
|
||||||
match projection.err().expect("expected failure").downcast().expect("expected specific error") {
|
match projection.err().expect("expected failure") {
|
||||||
ProjectorError::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`.");
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,15 +10,15 @@
|
||||||
|
|
||||||
use std; // To refer to std::result::Result.
|
use std; // To refer to std::result::Result.
|
||||||
|
|
||||||
|
use mentat_db::{
|
||||||
|
DbError,
|
||||||
|
};
|
||||||
|
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
Entid,
|
Entid,
|
||||||
};
|
};
|
||||||
|
|
||||||
use failure::{
|
pub type Result<T> = std::result::Result<T, PullError>;
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Debug, Fail)]
|
||||||
pub enum PullError {
|
pub enum PullError {
|
||||||
|
@ -27,4 +27,13 @@ pub enum PullError {
|
||||||
|
|
||||||
#[fail(display = ":db/id repeated")]
|
#[fail(display = ":db/id repeated")]
|
||||||
RepeatedDbId,
|
RepeatedDbId,
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
DbError(#[cause] DbError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DbError> for PullError {
|
||||||
|
fn from(error: DbError) -> PullError {
|
||||||
|
PullError::DbError(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,7 +102,7 @@ use mentat_query::{
|
||||||
|
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
|
||||||
use errors::{
|
pub use errors::{
|
||||||
PullError,
|
PullError,
|
||||||
Result,
|
Result,
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -16,8 +17,6 @@ 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::{
|
||||||
|
@ -30,4 +29,6 @@ pub use translate::{
|
||||||
query_to_select,
|
query_to_select,
|
||||||
};
|
};
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Error>;
|
// query-translator could be folded into query-projector; for now, just type alias the errors.
|
||||||
|
pub type TranslatorError = mentat_query_projector::ProjectorError;
|
||||||
|
pub type Result<T> = std::result::Result<T, TranslatorError>;
|
||||||
|
|
|
@ -521,5 +521,5 @@ pub fn query_to_select(schema: &Schema, query: AlgebraicQuery) -> Result<Project
|
||||||
projector: datalog_projector,
|
projector: datalog_projector,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}).map_err(|e| e.into())
|
})
|
||||||
}
|
}
|
||||||
|
|
41
src/conn.rs
41
src/conn.rs
|
@ -185,7 +185,7 @@ pub trait Pullable {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Syncable {
|
pub trait Syncable {
|
||||||
fn sync(&mut self, server_uri: &String, user_uuid: &String) -> Result<()>;
|
fn sync(&mut self, server_uri: &String, user_uuid: &String) -> ::std::result::Result<(), ::failure::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an in-progress, not yet committed, set of changes to the store.
|
/// Represents an in-progress, not yet committed, set of changes to the store.
|
||||||
|
@ -871,9 +871,9 @@ 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()).expect_err("expected transact error").downcast() {
|
match conn.transact(&mut sqlite, t.as_str()) {
|
||||||
Ok(::mentat_db::DbError::UnrecognizedEntid(e)) => {
|
Err(MentatError::DbError(e)) => {
|
||||||
assert_eq!(e, next + 1);
|
assert_eq!(e.kind(), ::mentat_db::DbErrorKind::UnrecognizedEntid(next + 1));
|
||||||
},
|
},
|
||||||
x => panic!("expected db error, got {:?}", x),
|
x => panic!("expected db error, got {:?}", x),
|
||||||
}
|
}
|
||||||
|
@ -898,10 +898,10 @@ 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()).expect_err("expected transact error").downcast() {
|
match conn.transact(&mut sqlite, t.as_str()) {
|
||||||
Ok(::mentat_db::DbError::UnrecognizedEntid(e)) => {
|
Err(MentatError::DbError(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.kind(), ::mentat_db::DbErrorKind::UnrecognizedEntid(next));
|
||||||
},
|
},
|
||||||
x => panic!("expected db error, got {:?}", x),
|
x => panic!("expected db error, got {:?}", x),
|
||||||
}
|
}
|
||||||
|
@ -1059,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.expect_err("expected transact to fail for bad edn").downcast() {
|
match report.expect_err("expected transact to fail for bad edn") {
|
||||||
Ok(edn::ParseError { .. }) => { },
|
MentatError::EdnParseError(_) => { },
|
||||||
Err(x) => panic!("expected EDN parse error, got {:?}", x),
|
x => panic!("expected EDN parse error, got {:?}", x),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Good EDN.
|
// Good EDN.
|
||||||
|
@ -1070,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.expect_err("expected transact error").downcast() {
|
match report.expect_err("expected transact error") {
|
||||||
Ok(edn::ParseError { .. }) => { },
|
MentatError::EdnParseError(_) => { },
|
||||||
Err(x) => panic!("expected EDN parse error, got {:?}", x),
|
x => panic!("expected EDN parse error, got {:?}", x),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Good transaction data.
|
// Good transaction data.
|
||||||
|
@ -1082,9 +1082,14 @@ 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.expect_err("expected transact error").downcast() {
|
match report.expect_err("expected transact error") {
|
||||||
Ok(::mentat_db::DbError::SchemaConstraintViolation(_)) => { },
|
MentatError::DbError(e) => {
|
||||||
x => panic!("expected schema constraint violation, got {:?}", x),
|
match e.kind() {
|
||||||
|
::mentat_db::DbErrorKind::SchemaConstraintViolation(_) => {},
|
||||||
|
_ => panic!("expected SchemaConstraintViolation"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
x => panic!("expected db error, got {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1101,8 +1106,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.expect_err("expected cache to fail").downcast() {
|
match res.expect_err("expected cache to fail") {
|
||||||
Ok(MentatError::UnknownAttribute(msg)) => assert_eq!(msg, ":foo/bat"),
|
MentatError::UnknownAttribute(msg) => assert_eq!(msg, ":foo/bat"),
|
||||||
x => panic!("expected UnknownAttribute error, got {:?}", x),
|
x => panic!("expected UnknownAttribute error, got {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -380,9 +380,6 @@ impl FromThing<KnownEntid> for TypedValueOr<TempIdHandle> {
|
||||||
mod testing {
|
mod testing {
|
||||||
extern crate mentat_db;
|
extern crate mentat_db;
|
||||||
|
|
||||||
// For matching inside a test.
|
|
||||||
use mentat_db::DbError;
|
|
||||||
|
|
||||||
use ::{
|
use ::{
|
||||||
Conn,
|
Conn,
|
||||||
Entid,
|
Entid,
|
||||||
|
@ -422,10 +419,11 @@ 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 Ok(DbError::UnrecognizedEntid(e)) = in_progress.transact_terms(terms, tempids).expect_err("expected transact to fail").downcast() {
|
match in_progress.transact_terms(terms, tempids).expect_err("expected transact to fail") {
|
||||||
assert_eq!(e, 999);
|
MentatError::DbError(e) => {
|
||||||
} else {
|
assert_eq!(e.kind(), mentat_db::DbErrorKind::UnrecognizedEntid(999));
|
||||||
panic!("Should have rejected the entid.");
|
},
|
||||||
|
_ => panic!("Should have rejected the entid."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,16 +14,23 @@ use std; // To refer to std::result::Result.
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use failure::Error;
|
use rusqlite;
|
||||||
|
|
||||||
|
use edn;
|
||||||
|
|
||||||
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_sql;
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, MentatError>;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! bail {
|
macro_rules! bail {
|
||||||
|
@ -34,6 +41,9 @@ macro_rules! bail {
|
||||||
|
|
||||||
#[derive(Debug, Fail)]
|
#[derive(Debug, Fail)]
|
||||||
pub enum MentatError {
|
pub enum MentatError {
|
||||||
|
#[fail(display = "bad uuid {}", _0)]
|
||||||
|
BadUuid(String),
|
||||||
|
|
||||||
#[fail(display = "path {} already exists", _0)]
|
#[fail(display = "path {} already exists", _0)]
|
||||||
PathAlreadyExists(String),
|
PathAlreadyExists(String),
|
||||||
|
|
||||||
|
@ -69,4 +79,78 @@ pub enum MentatError {
|
||||||
|
|
||||||
#[fail(display = "provided value of type {} doesn't match attribute value type {}", _0, _1)]
|
#[fail(display = "provided value of type {} doesn't match attribute value type {}", _0, _1)]
|
||||||
ValueTypeMismatch(ValueType, ValueType),
|
ValueTypeMismatch(ValueType, ValueType),
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
IoError(#[cause] std::io::Error),
|
||||||
|
|
||||||
|
// It would be better to capture the underlying `rusqlite::Error`, but that type doesn't
|
||||||
|
// implement many useful traits, including `Clone`, `Eq`, and `PartialEq`.
|
||||||
|
#[fail(display = "SQL error: _0")]
|
||||||
|
RusqliteError(String),
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
EdnParseError(#[cause] edn::ParseError),
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
DbError(#[cause] mentat_db::DbError),
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
AlgebrizerError(#[cause] mentat_query_algebrizer::AlgebrizerError),
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
ProjectorError(#[cause] mentat_query_projector::ProjectorError),
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
PullError(#[cause] mentat_query_pull::PullError),
|
||||||
|
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
SQLError(#[cause] mentat_sql::SQLError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for MentatError {
|
||||||
|
fn from(error: std::io::Error) -> MentatError {
|
||||||
|
MentatError::IoError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<rusqlite::Error> for MentatError {
|
||||||
|
fn from(error: rusqlite::Error) -> MentatError {
|
||||||
|
MentatError::RusqliteError(error.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<edn::ParseError> for MentatError {
|
||||||
|
fn from(error: edn::ParseError) -> MentatError {
|
||||||
|
MentatError::EdnParseError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mentat_db::DbError> for MentatError {
|
||||||
|
fn from(error: mentat_db::DbError) -> MentatError {
|
||||||
|
MentatError::DbError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mentat_query_algebrizer::AlgebrizerError> for MentatError {
|
||||||
|
fn from(error: mentat_query_algebrizer::AlgebrizerError) -> MentatError {
|
||||||
|
MentatError::AlgebrizerError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mentat_query_projector::ProjectorError> for MentatError {
|
||||||
|
fn from(error: mentat_query_projector::ProjectorError) -> MentatError {
|
||||||
|
MentatError::ProjectorError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mentat_query_pull::PullError> for MentatError {
|
||||||
|
fn from(error: mentat_query_pull::PullError) -> MentatError {
|
||||||
|
MentatError::PullError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mentat_sql::SQLError> for MentatError {
|
||||||
|
fn from(error: mentat_sql::SQLError) -> MentatError {
|
||||||
|
MentatError::SQLError(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -104,6 +104,18 @@ macro_rules! kw {
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
pub use errors::{
|
||||||
|
MentatError,
|
||||||
|
Result,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use edn::ParseError;
|
||||||
|
pub use mentat_db::DbError;
|
||||||
|
pub use mentat_query_algebrizer::AlgebrizerError;
|
||||||
|
pub use mentat_query_projector::ProjectorError;
|
||||||
|
pub use mentat_query_pull::PullError;
|
||||||
|
pub use mentat_sql::SQLError;
|
||||||
|
|
||||||
pub mod conn;
|
pub mod conn;
|
||||||
pub mod entity_builder;
|
pub mod entity_builder;
|
||||||
pub mod query;
|
pub mod query;
|
||||||
|
|
|
@ -234,8 +234,8 @@ impl Pullable for Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Syncable for Store {
|
impl Syncable for Store {
|
||||||
fn sync(&mut self, server_uri: &String, user_uuid: &String) -> Result<()> {
|
fn sync(&mut self, server_uri: &String, user_uuid: &String) -> ::std::result::Result<(), ::failure::Error> {
|
||||||
let uuid = Uuid::parse_str(&user_uuid)?;
|
let uuid = Uuid::parse_str(&user_uuid).map_err(|_| MentatError::BadUuid(user_uuid.clone()))?;
|
||||||
Ok(Syncer::flow(&mut self.sqlite, server_uri, &uuid)?)
|
Ok(Syncer::flow(&mut self.sqlite, server_uri, &uuid)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,7 +233,7 @@ 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.expect_err("expected unbound variables").downcast().expect("expected specific error") {
|
match results.expect_err("expected unbound variables") {
|
||||||
MentatError::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());
|
||||||
},
|
},
|
||||||
|
@ -411,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.expect_err("expected query to fail").downcast() {
|
match r.expect_err("expected query to fail") {
|
||||||
Ok(mentat_query_algebrizer::AlgebrizerError::InvalidArgument(PlainSymbol(s), ty, i)) => {
|
MentatError::AlgebrizerError(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);
|
||||||
|
@ -426,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.expect_err("expected query to fail").downcast() {
|
match r.expect_err("expected query to fail") {
|
||||||
Ok(mentat_query_algebrizer::AlgebrizerError::InvalidArgument(PlainSymbol(s), ty, i)) => {
|
MentatError::AlgebrizerError(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);
|
||||||
|
@ -582,8 +582,8 @@ 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.expect_err("expected query to fail").downcast() {
|
match r.expect_err("expected query to fail") {
|
||||||
Ok(::mentat_query_projector::errors::ProjectorError::CannotApplyAggregateOperationToTypes(
|
MentatError::ProjectorError(::mentat_query_projector::errors::ProjectorError::CannotApplyAggregateOperationToTypes(
|
||||||
SimpleAggregationOp::Sum, types)) => {
|
SimpleAggregationOp::Sum, types)) => {
|
||||||
assert_eq!(types, all_types);
|
assert_eq!(types, all_types);
|
||||||
},
|
},
|
||||||
|
@ -594,8 +594,8 @@ fn test_aggregates_type_handling() {
|
||||||
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.expect_err("expected query to fail").downcast() {
|
match r.expect_err("expected query to fail") {
|
||||||
Ok(::mentat_query_projector::errors::ProjectorError::CannotApplyAggregateOperationToTypes(
|
MentatError::ProjectorError(::mentat_query_projector::errors::ProjectorError::CannotApplyAggregateOperationToTypes(
|
||||||
SimpleAggregationOp::Sum,
|
SimpleAggregationOp::Sum,
|
||||||
types)) => {
|
types)) => {
|
||||||
assert_eq!(types, ValueTypeSet::of_one(ValueType::Instant));
|
assert_eq!(types, ValueTypeSet::of_one(ValueType::Instant));
|
||||||
|
@ -1336,8 +1336,8 @@ 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.expect_err("expected query to fail").downcast() {
|
match res.expect_err("expected query to fail") {
|
||||||
Ok(::mentat_query_projector::errors::ProjectorError::AmbiguousAggregates(mmc, cc)) => {
|
MentatError::ProjectorError(::mentat_query_projector::errors::ProjectorError::AmbiguousAggregates(mmc, cc)) => {
|
||||||
assert_eq!(mmc, 2);
|
assert_eq!(mmc, 2);
|
||||||
assert_eq!(cc, 1);
|
assert_eq!(cc, 1);
|
||||||
},
|
},
|
||||||
|
|
|
@ -286,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).expect_err("expected vocabulary to fail").downcast() {
|
match in_progress.ensure_vocabulary(&foo_v1_malformed).expect_err("expected vocabulary to fail") {
|
||||||
Ok(MentatError::ConflictingAttributeDefinitions(vocab, version, attr, theirs, ours)) => {
|
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);
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use failure::{
|
use failure::{
|
||||||
err_msg,
|
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -404,7 +403,8 @@ 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(_) => return Err(err_msg(".open_encrypted and .empty_encrypted require the sqlcipher Mentat feature")),
|
Some(_) => return Err(::mentat::MentatError::RusqliteError(".open_encrypted and .empty_encrypted require the sqlcipher Mentat feature".into())),
|
||||||
|
|
||||||
#[cfg(feature = "sqlcipher")]
|
#[cfg(feature = "sqlcipher")]
|
||||||
Some(k) => {
|
Some(k) => {
|
||||||
if empty {
|
if empty {
|
||||||
|
|
Loading…
Reference in a new issue