Convert db/ to failure.
This commit is contained in:
parent
0adfa6aae6
commit
31de5be64f
14 changed files with 326 additions and 262 deletions
|
@ -6,6 +6,7 @@ workspace = ".."
|
|||
[dependencies]
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
enum-set = { git = "https://github.com/rnewman/enum-set" }
|
||||
failure = "0.1.1"
|
||||
indexmap = "1"
|
||||
lazy_static = "0.2"
|
||||
num = "0.1"
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
/// Cache traits.
|
||||
|
||||
use failure;
|
||||
|
||||
use std::collections::{
|
||||
BTreeSet,
|
||||
};
|
||||
|
@ -33,8 +35,7 @@ pub trait CachedAttributes {
|
|||
fn get_entids_for_value(&self, attribute: Entid, value: &TypedValue) -> Option<&BTreeSet<Entid>>;
|
||||
}
|
||||
|
||||
pub trait UpdateableCache {
|
||||
type Error;
|
||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<(), Self::Error>
|
||||
pub trait UpdateableCache<Error=failure::Error> {
|
||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<(), Error>
|
||||
where I: Iterator<Item=(Entid, Entid, TypedValue)>;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
extern crate chrono;
|
||||
extern crate enum_set;
|
||||
extern crate failure;
|
||||
extern crate indexmap;
|
||||
extern crate ordered_float;
|
||||
extern crate uuid;
|
||||
|
|
|
@ -8,7 +8,8 @@ default = []
|
|||
sqlcipher = ["rusqlite/sqlcipher"]
|
||||
|
||||
[dependencies]
|
||||
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
|
||||
failure = "0.1.1"
|
||||
failure_derive = "0.1.1"
|
||||
indexmap = "1"
|
||||
itertools = "0.7"
|
||||
lazy_static = "0.2"
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use edn;
|
||||
use errors::{ErrorKind, Result};
|
||||
use errors::{
|
||||
DbError,
|
||||
Result,
|
||||
};
|
||||
use edn::types::Value;
|
||||
use edn::symbols;
|
||||
use entids;
|
||||
|
@ -156,7 +159,7 @@ lazy_static! {
|
|||
:db/cardinality :db.cardinality/many}}"#;
|
||||
edn::parse::value(s)
|
||||
.map(|v| v.without_spans())
|
||||
.map_err(|_| ErrorKind::BadBootstrapDefinition("Unable to parse V1_SYMBOLIC_SCHEMA".into()))
|
||||
.map_err(|_| DbError::BadBootstrapDefinition("Unable to parse V1_SYMBOLIC_SCHEMA".into()))
|
||||
.unwrap()
|
||||
};
|
||||
}
|
||||
|
@ -207,14 +210,14 @@ fn symbolic_schema_to_triples(ident_map: &IdentMap, symbolic_schema: &Value) ->
|
|||
for (ident, mp) in m {
|
||||
let ident = match ident {
|
||||
&Value::Keyword(ref ident) => ident,
|
||||
_ => bail!(ErrorKind::BadBootstrapDefinition(format!("Expected namespaced keyword for ident but got '{:?}'", ident))),
|
||||
_ => bail!(DbError::BadBootstrapDefinition(format!("Expected namespaced keyword for ident but got '{:?}'", ident))),
|
||||
};
|
||||
match *mp {
|
||||
Value::Map(ref mpp) => {
|
||||
for (attr, value) in mpp {
|
||||
let attr = match attr {
|
||||
&Value::Keyword(ref attr) => attr,
|
||||
_ => bail!(ErrorKind::BadBootstrapDefinition(format!("Expected namespaced keyword for attr but got '{:?}'", attr))),
|
||||
_ => bail!(DbError::BadBootstrapDefinition(format!("Expected namespaced keyword for attr but got '{:?}'", attr))),
|
||||
};
|
||||
|
||||
// We have symbolic idents but the transactor handles entids. Ad-hoc
|
||||
|
@ -229,20 +232,20 @@ fn symbolic_schema_to_triples(ident_map: &IdentMap, symbolic_schema: &Value) ->
|
|||
Some(TypedValue::Keyword(ref k)) => {
|
||||
ident_map.get(k)
|
||||
.map(|entid| TypedValue::Ref(*entid))
|
||||
.ok_or(ErrorKind::UnrecognizedIdent(k.to_string()))?
|
||||
.ok_or(DbError::UnrecognizedIdent(k.to_string()))?
|
||||
},
|
||||
Some(v) => v,
|
||||
_ => bail!(ErrorKind::BadBootstrapDefinition(format!("Expected Mentat typed value for value but got '{:?}'", value)))
|
||||
_ => bail!(DbError::BadBootstrapDefinition(format!("Expected Mentat typed value for value but got '{:?}'", value)))
|
||||
};
|
||||
|
||||
triples.push((ident.clone(), attr.clone(), typed_value));
|
||||
}
|
||||
},
|
||||
_ => bail!(ErrorKind::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into()))
|
||||
_ => bail!(DbError::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into()))
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => bail!(ErrorKind::BadBootstrapDefinition("Expected {...}".into()))
|
||||
_ => bail!(DbError::BadBootstrapDefinition("Expected {...}".into()))
|
||||
}
|
||||
Ok(triples)
|
||||
}
|
||||
|
@ -263,11 +266,11 @@ fn symbolic_schema_to_assertions(symbolic_schema: &Value) -> Result<Vec<Value>>
|
|||
value.clone()]));
|
||||
}
|
||||
},
|
||||
_ => bail!(ErrorKind::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into()))
|
||||
_ => bail!(DbError::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into()))
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => bail!(ErrorKind::BadBootstrapDefinition("Expected {...}".into()))
|
||||
_ => bail!(DbError::BadBootstrapDefinition("Expected {...}".into()))
|
||||
}
|
||||
Ok(assertions)
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ use db::{
|
|||
};
|
||||
|
||||
use errors::{
|
||||
ErrorKind,
|
||||
DbError,
|
||||
Result,
|
||||
};
|
||||
|
||||
|
@ -1155,15 +1155,14 @@ impl CachedAttributes for AttributeCaches {
|
|||
}
|
||||
|
||||
impl UpdateableCache for AttributeCaches {
|
||||
type Error = ::errors::Error;
|
||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> ::std::result::Result<(), Self::Error>
|
||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
||||
where I: Iterator<Item=(Entid, Entid, TypedValue)> {
|
||||
self.update_with_fallback(None, schema, retractions, assertions)
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributeCaches {
|
||||
fn update_with_fallback<I>(&mut self, fallback: Option<&AttributeCaches>, schema: &Schema, retractions: I, assertions: I) -> ::std::result::Result<(), ::errors::Error>
|
||||
fn update_with_fallback<I>(&mut self, fallback: Option<&AttributeCaches>, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
||||
where I: Iterator<Item=(Entid, Entid, TypedValue)> {
|
||||
let r_aevs = retractions.peekable();
|
||||
self.accumulate_into_cache(fallback, schema, r_aevs, AccumulationBehavior::Remove)?;
|
||||
|
@ -1237,7 +1236,7 @@ impl SQLiteAttributeCache {
|
|||
let a = attribute.into();
|
||||
|
||||
// The attribute must exist!
|
||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| ErrorKind::UnknownAttribute(a))?;
|
||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbError::UnknownAttribute(a))?;
|
||||
let caches = self.make_mut();
|
||||
caches.forward_cached_attributes.insert(a);
|
||||
caches.repopulate(schema, sqlite, a)
|
||||
|
@ -1248,7 +1247,7 @@ impl SQLiteAttributeCache {
|
|||
let a = attribute.into();
|
||||
|
||||
// The attribute must exist!
|
||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| ErrorKind::UnknownAttribute(a))?;
|
||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbError::UnknownAttribute(a))?;
|
||||
|
||||
let caches = self.make_mut();
|
||||
caches.reverse_cached_attributes.insert(a);
|
||||
|
@ -1278,8 +1277,7 @@ impl SQLiteAttributeCache {
|
|||
}
|
||||
|
||||
impl UpdateableCache for SQLiteAttributeCache {
|
||||
type Error = ::errors::Error;
|
||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> ::std::result::Result<(), Self::Error>
|
||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
||||
where I: Iterator<Item=(Entid, Entid, TypedValue)> {
|
||||
self.make_mut().update(schema, retractions, assertions)
|
||||
}
|
||||
|
@ -1356,7 +1354,7 @@ impl InProgressSQLiteAttributeCache {
|
|||
let a = attribute.into();
|
||||
|
||||
// The attribute must exist!
|
||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| ErrorKind::UnknownAttribute(a))?;
|
||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbError::UnknownAttribute(a))?;
|
||||
|
||||
if self.is_attribute_cached_forward(a) {
|
||||
return Ok(());
|
||||
|
@ -1372,7 +1370,7 @@ impl InProgressSQLiteAttributeCache {
|
|||
let a = attribute.into();
|
||||
|
||||
// The attribute must exist!
|
||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| ErrorKind::UnknownAttribute(a))?;
|
||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbError::UnknownAttribute(a))?;
|
||||
|
||||
if self.is_attribute_cached_reverse(a) {
|
||||
return Ok(());
|
||||
|
@ -1388,7 +1386,7 @@ impl InProgressSQLiteAttributeCache {
|
|||
let a = attribute.into();
|
||||
|
||||
// The attribute must exist!
|
||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| ErrorKind::UnknownAttribute(a))?;
|
||||
let _ = schema.attribute_for_entid(a).ok_or_else(|| DbError::UnknownAttribute(a))?;
|
||||
|
||||
// TODO: reverse-index unique by default?
|
||||
let reverse_done = self.is_attribute_cached_reverse(a);
|
||||
|
@ -1427,8 +1425,7 @@ impl InProgressSQLiteAttributeCache {
|
|||
}
|
||||
|
||||
impl UpdateableCache for InProgressSQLiteAttributeCache {
|
||||
type Error = ::errors::Error;
|
||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> ::std::result::Result<(), Self::Error>
|
||||
fn update<I>(&mut self, schema: &Schema, retractions: I, assertions: I) -> Result<()>
|
||||
where I: Iterator<Item=(Entid, Entid, TypedValue)> {
|
||||
self.overlay.update_with_fallback(Some(&self.inner), schema, retractions, assertions)
|
||||
}
|
||||
|
|
92
db/src/db.rs
92
db/src/db.rs
|
@ -10,6 +10,8 @@
|
|||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use failure::ResultExt;
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::{
|
||||
|
@ -53,7 +55,11 @@ use mentat_core::{
|
|||
ValueRc,
|
||||
};
|
||||
|
||||
use errors::{ErrorKind, Result, ResultExt};
|
||||
use errors::{
|
||||
DbError,
|
||||
Result,
|
||||
DbSqlErrorKind,
|
||||
};
|
||||
use metadata;
|
||||
use schema::{
|
||||
SchemaBuilding,
|
||||
|
@ -251,8 +257,8 @@ lazy_static! {
|
|||
/// documentation](https://www.sqlite.org/pragma.html#pragma_user_version).
|
||||
fn set_user_version(conn: &rusqlite::Connection, version: i32) -> Result<()> {
|
||||
conn.execute(&format!("PRAGMA user_version = {}", version), &[])
|
||||
.chain_err(|| "Could not set_user_version")
|
||||
.map(|_| ())
|
||||
.context(DbSqlErrorKind::CouldNotSetVersionPragma)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the SQLite user version.
|
||||
|
@ -260,10 +266,10 @@ fn set_user_version(conn: &rusqlite::Connection, version: i32) -> Result<()> {
|
|||
/// Mentat manages its own SQL schema version using the user version. See the [SQLite
|
||||
/// documentation](https://www.sqlite.org/pragma.html#pragma_user_version).
|
||||
fn get_user_version(conn: &rusqlite::Connection) -> Result<i32> {
|
||||
conn.query_row("PRAGMA user_version", &[], |row| {
|
||||
let v = conn.query_row("PRAGMA user_version", &[], |row| {
|
||||
row.get(0)
|
||||
})
|
||||
.chain_err(|| "Could not get_user_version")
|
||||
}).context(DbSqlErrorKind::CouldNotGetVersionPragma)?;
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
/// Do just enough work that either `create_current_version` or sync can populate the DB.
|
||||
|
@ -303,8 +309,7 @@ pub fn create_current_version(conn: &mut rusqlite::Connection) -> Result<DB> {
|
|||
// TODO: validate metadata mutations that aren't schema related, like additional partitions.
|
||||
if let Some(next_schema) = next_schema {
|
||||
if next_schema != db.schema {
|
||||
// TODO Use custom ErrorKind https://github.com/brson/error-chain/issues/117
|
||||
bail!(ErrorKind::NotYetImplemented(format!("Initial bootstrap transaction did not produce expected bootstrap schema")));
|
||||
bail!(DbError::NotYetImplemented(format!("Initial bootstrap transaction did not produce expected bootstrap schema")));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,7 +331,7 @@ pub fn ensure_current_version(conn: &mut rusqlite::Connection) -> Result<DB> {
|
|||
CURRENT_VERSION => read_db(conn),
|
||||
|
||||
// TODO: support updating an existing store.
|
||||
v => bail!(ErrorKind::NotYetImplemented(format!("Opening databases with Mentat version: {}", v))),
|
||||
v => bail!(DbError::NotYetImplemented(format!("Opening databases with Mentat version: {}", v))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,7 +361,7 @@ impl TypedSQLValue for TypedValue {
|
|||
let u = Uuid::from_bytes(x.as_slice());
|
||||
if u.is_err() {
|
||||
// Rather than exposing Uuid's ParseError…
|
||||
bail!(ErrorKind::BadSQLValuePair(rusqlite::types::Value::Blob(x),
|
||||
bail!(DbError::BadSQLValuePair(rusqlite::types::Value::Blob(x),
|
||||
value_type_tag));
|
||||
}
|
||||
Ok(TypedValue::Uuid(u.unwrap()))
|
||||
|
@ -364,7 +369,7 @@ impl TypedSQLValue for TypedValue {
|
|||
(13, rusqlite::types::Value::Text(x)) => {
|
||||
to_namespaced_keyword(&x).map(|k| k.into())
|
||||
},
|
||||
(_, value) => bail!(ErrorKind::BadSQLValuePair(value, value_type_tag)),
|
||||
(_, value) => bail!(DbError::BadSQLValuePair(value, value_type_tag)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,12 +452,12 @@ fn read_ident_map(conn: &rusqlite::Connection) -> Result<IdentMap> {
|
|||
let v = read_materialized_view(conn, "idents")?;
|
||||
v.into_iter().map(|(e, a, typed_value)| {
|
||||
if a != entids::DB_IDENT {
|
||||
bail!(ErrorKind::NotYetImplemented(format!("bad idents materialized view: expected :db/ident but got {}", a)));
|
||||
bail!(DbError::NotYetImplemented(format!("bad idents materialized view: expected :db/ident but got {}", a)));
|
||||
}
|
||||
if let TypedValue::Keyword(keyword) = typed_value {
|
||||
Ok((keyword.as_ref().clone(), e))
|
||||
} else {
|
||||
bail!(ErrorKind::NotYetImplemented(format!("bad idents materialized view: expected [entid :db/ident keyword] but got [entid :db/ident {:?}]", typed_value)));
|
||||
bail!(DbError::NotYetImplemented(format!("bad idents materialized view: expected [entid :db/ident keyword] but got [entid :db/ident {:?}]", typed_value)));
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
|
@ -546,9 +551,8 @@ fn search(conn: &rusqlite::Connection) -> Result<()> {
|
|||
t.a0 = d.a"#;
|
||||
|
||||
let mut stmt = conn.prepare_cached(s)?;
|
||||
stmt.execute(&[])
|
||||
.map(|_c| ())
|
||||
.chain_err(|| "Could not search!")
|
||||
stmt.execute(&[]).context(DbSqlErrorKind::CouldNotSearch)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Insert the new transaction into the `transactions` table.
|
||||
|
@ -570,9 +574,7 @@ fn insert_transaction(conn: &rusqlite::Connection, tx: Entid) -> Result<()> {
|
|||
WHERE added0 IS 1 AND ((rid IS NULL) OR ((rid IS NOT NULL) AND (v0 IS NOT v)))"#;
|
||||
|
||||
let mut stmt = conn.prepare_cached(s)?;
|
||||
stmt.execute(&[&tx])
|
||||
.map(|_c| ())
|
||||
.chain_err(|| "Could not insert transaction: failed to add datoms not already present")?;
|
||||
stmt.execute(&[&tx]).context(DbSqlErrorKind::TxInsertFailedToAddMissingDatoms)?;
|
||||
|
||||
let s = r#"
|
||||
INSERT INTO transactions (e, a, v, tx, added, value_type_tag)
|
||||
|
@ -583,9 +585,7 @@ fn insert_transaction(conn: &rusqlite::Connection, tx: Entid) -> Result<()> {
|
|||
(added0 IS 1 AND search_type IS ':db.cardinality/one' AND v0 IS NOT v))"#;
|
||||
|
||||
let mut stmt = conn.prepare_cached(s)?;
|
||||
stmt.execute(&[&tx])
|
||||
.map(|_c| ())
|
||||
.chain_err(|| "Could not insert transaction: failed to retract datoms already present")?;
|
||||
stmt.execute(&[&tx]).context(DbSqlErrorKind::TxInsertFailedToRetractDatoms)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -607,9 +607,7 @@ fn update_datoms(conn: &rusqlite::Connection, tx: Entid) -> Result<()> {
|
|||
DELETE FROM datoms WHERE rowid IN ids"#;
|
||||
|
||||
let mut stmt = conn.prepare_cached(s)?;
|
||||
stmt.execute(&[])
|
||||
.map(|_c| ())
|
||||
.chain_err(|| "Could not update datoms: failed to retract datoms already present")?;
|
||||
stmt.execute(&[]).context(DbSqlErrorKind::DatomsUpdateFailedToRetract)?;
|
||||
|
||||
// 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
|
||||
|
@ -632,10 +630,7 @@ fn update_datoms(conn: &rusqlite::Connection, tx: Entid) -> Result<()> {
|
|||
AttributeBitFlags::UniqueValue as u8);
|
||||
|
||||
let mut stmt = conn.prepare_cached(&s)?;
|
||||
stmt.execute(&[&tx])
|
||||
.map(|_c| ())
|
||||
.chain_err(|| "Could not update datoms: failed to add datoms not already present")?;
|
||||
|
||||
stmt.execute(&[&tx]).context(DbSqlErrorKind::DatomsUpdateFailedToAdd)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -762,9 +757,7 @@ impl MentatStoring for rusqlite::Connection {
|
|||
|
||||
for statement in &statements {
|
||||
let mut stmt = self.prepare_cached(statement)?;
|
||||
stmt.execute(&[])
|
||||
.map(|_c| ())
|
||||
.chain_err(|| "Failed to create temporary tables")?;
|
||||
stmt.execute(&[]).context(DbSqlErrorKind::FailedToCreateTempTables)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -827,8 +820,9 @@ impl MentatStoring for rusqlite::Connection {
|
|||
// TODO: consider ensuring we inserted the expected number of rows.
|
||||
let mut stmt = self.prepare_cached(s.as_str())?;
|
||||
stmt.execute(¶ms)
|
||||
.context(DbSqlErrorKind::NonFtsInsertionIntoTempSearchTableFailed)
|
||||
.map_err(|e| e.into())
|
||||
.map(|_c| ())
|
||||
.chain_err(|| "Could not insert non-fts one statements into temporary search table!")
|
||||
}).collect::<Result<Vec<()>>>();
|
||||
|
||||
results.map(|_| ())
|
||||
|
@ -886,7 +880,7 @@ impl MentatStoring for rusqlite::Connection {
|
|||
}
|
||||
},
|
||||
_ => {
|
||||
bail!("Cannot transact a fulltext assertion with a typed value that is not :db/valueType :db.type/string");
|
||||
bail!(DbError::WrongTypeValueForFtsAssertion);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -913,9 +907,7 @@ impl MentatStoring for rusqlite::Connection {
|
|||
|
||||
// TODO: consider ensuring we inserted the expected number of rows.
|
||||
let mut stmt = self.prepare_cached(fts_s.as_str())?;
|
||||
stmt.execute(&fts_params)
|
||||
.map(|_c| ())
|
||||
.chain_err(|| "Could not insert fts values into fts table!")?;
|
||||
stmt.execute(&fts_params).context(DbSqlErrorKind::FtsInsertionFailed)?;
|
||||
|
||||
// Second, insert searches.
|
||||
// `params` reference computed values in `block`.
|
||||
|
@ -943,17 +935,14 @@ impl MentatStoring for rusqlite::Connection {
|
|||
|
||||
// TODO: consider ensuring we inserted the expected number of rows.
|
||||
let mut stmt = self.prepare_cached(s.as_str())?;
|
||||
stmt.execute(¶ms)
|
||||
stmt.execute(¶ms).context(DbSqlErrorKind::FtsInsertionIntoTempSearchTableFailed)
|
||||
.map_err(|e| e.into())
|
||||
.map(|_c| ())
|
||||
.chain_err(|| "Could not insert FTS statements into temporary search table!")
|
||||
}).collect::<Result<Vec<()>>>();
|
||||
|
||||
// Finally, clean up temporary searchids.
|
||||
let mut stmt = self.prepare_cached("UPDATE fulltext_values SET searchid = NULL WHERE searchid IS NOT NULL")?;
|
||||
stmt.execute(&[])
|
||||
.map(|_c| ())
|
||||
.chain_err(|| "Could not drop FTS search ids!")?;
|
||||
|
||||
stmt.execute(&[]).context(DbSqlErrorKind::FtsFailedToDropSearchIds)?;
|
||||
results.map(|_| ())
|
||||
}
|
||||
|
||||
|
@ -985,7 +974,7 @@ pub fn update_partition_map(conn: &rusqlite::Connection, partition_map: &Partiti
|
|||
let max_vars = conn.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER) as usize;
|
||||
let max_partitions = max_vars / values_per_statement;
|
||||
if partition_map.len() > max_partitions {
|
||||
bail!(ErrorKind::NotYetImplemented(format!("No more than {} partitions are supported", max_partitions)));
|
||||
bail!(DbError::NotYetImplemented(format!("No more than {} partitions are supported", max_partitions)));
|
||||
}
|
||||
|
||||
// Like "UPDATE parts SET idx = CASE WHEN part = ? THEN ? WHEN part = ? THEN ? ELSE idx END".
|
||||
|
@ -1001,9 +990,8 @@ pub fn update_partition_map(conn: &rusqlite::Connection, partition_map: &Partiti
|
|||
// supported in the Clojure implementation at all, and might not be supported in Mentat soon,
|
||||
// so this is very low priority.
|
||||
let mut stmt = conn.prepare_cached(s.as_str())?;
|
||||
stmt.execute(¶ms[..])
|
||||
.map(|_c| ())
|
||||
.chain_err(|| "Could not update partition map")
|
||||
stmt.execute(¶ms[..]).context(DbSqlErrorKind::FailedToUpdatePartitionMap)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update the metadata materialized views based on the given metadata report.
|
||||
|
@ -1062,8 +1050,8 @@ SELECT EXISTS
|
|||
// error message in this case.
|
||||
if unique_value_stmt.execute(&[to_bool_ref(attribute.unique.is_some()), &entid as &ToSql]).is_err() {
|
||||
match attribute.unique {
|
||||
Some(attribute::Unique::Value) => bail!(ErrorKind::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.unique/value", entid))),
|
||||
Some(attribute::Unique::Identity) => bail!(ErrorKind::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.unique/identity", entid))),
|
||||
Some(attribute::Unique::Value) => bail!(DbError::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))),
|
||||
None => unreachable!(), // This shouldn't happen, even after we support removing :db/unique.
|
||||
}
|
||||
}
|
||||
|
@ -1077,7 +1065,7 @@ SELECT EXISTS
|
|||
if !attribute.multival {
|
||||
let mut rows = cardinality_stmt.query(&[&entid as &ToSql])?;
|
||||
if rows.next().is_some() {
|
||||
bail!(ErrorKind::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.cardinality/one", entid)));
|
||||
bail!(DbError::SchemaAlterationFailed(format!("Cannot alter schema attribute {} to be :db.cardinality/one", entid)));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2684,8 +2672,8 @@ mod tests {
|
|||
|
||||
let report = conn.transact_simple_terms(terms, InternSet::new());
|
||||
|
||||
match report.unwrap_err() {
|
||||
errors::Error(ErrorKind::SchemaConstraintViolation(errors::SchemaConstraintViolation::TypeDisagreements { conflicting_datoms }), _) => {
|
||||
match report.unwrap_err().downcast() {
|
||||
Ok(DbError::SchemaConstraintViolation(errors::SchemaConstraintViolation::TypeDisagreements { conflicting_datoms })) => {
|
||||
let mut map = BTreeMap::default();
|
||||
map.insert((100, entids::DB_TX_INSTANT, TypedValue::Long(-1)), ValueType::Instant);
|
||||
map.insert((200, entids::DB_IDENT, TypedValue::typed_string("test")), ValueType::Keyword);
|
||||
|
|
246
db/src/errors.rs
246
db/src/errors.rs
|
@ -10,6 +10,13 @@
|
|||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use failure::{
|
||||
Backtrace,
|
||||
Context,
|
||||
Error,
|
||||
Fail,
|
||||
};
|
||||
|
||||
use std::collections::{
|
||||
BTreeMap,
|
||||
BTreeSet,
|
||||
|
@ -29,6 +36,16 @@ use types::{
|
|||
ValueType,
|
||||
};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! bail {
|
||||
($e:expr) => (
|
||||
return Err($e.into());
|
||||
)
|
||||
}
|
||||
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
// TODO Error/ErrorKind pair
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum CardinalityConflict {
|
||||
/// A cardinality one attribute has multiple assertions `[e a v1], [e a v2], ...`.
|
||||
|
@ -46,7 +63,8 @@ pub enum CardinalityConflict {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
// TODO Error/ErrorKind pair
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Fail)]
|
||||
pub enum SchemaConstraintViolation {
|
||||
/// A transaction tried to assert datoms where one tempid upserts to two (or more) distinct
|
||||
/// entids.
|
||||
|
@ -125,95 +143,145 @@ impl ::std::fmt::Display for InputError {
|
|||
}
|
||||
}
|
||||
|
||||
error_chain! {
|
||||
types {
|
||||
Error, ErrorKind, ResultExt, Result;
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum DbError {
|
||||
/// We're just not done yet. Message that the feature is recognized but not yet
|
||||
/// implemented.
|
||||
#[fail(display = "not yet implemented: {}", _0)]
|
||||
NotYetImplemented(String),
|
||||
|
||||
/// We've been given a value that isn't the correct Mentat type.
|
||||
#[fail(display = "value '{}' is not the expected Mentat value type {:?}", _0, _1)]
|
||||
BadValuePair(String, ValueType),
|
||||
|
||||
/// We've got corrupt data in the SQL store: a value and value_type_tag don't line up.
|
||||
/// TODO _1.data_type()
|
||||
#[fail(display = "bad SQL (value_type_tag, value) pair: ({:?}, {:?})", _0, _1)]
|
||||
BadSQLValuePair(rusqlite::types::Value, i32),
|
||||
|
||||
// /// The SQLite store user_version isn't recognized. This could be an old version of Mentat
|
||||
// /// trying to open a newer version SQLite store; or it could be a corrupt file; or ...
|
||||
// #[fail(display = "bad SQL store user_version: {}", _0)]
|
||||
// BadSQLiteStoreVersion(i32),
|
||||
|
||||
/// A bootstrap definition couldn't be parsed or installed. This is a programmer error, not
|
||||
/// a runtime error.
|
||||
#[fail(display = "bad bootstrap definition: {}", _0)]
|
||||
BadBootstrapDefinition(String),
|
||||
|
||||
/// A schema assertion couldn't be parsed.
|
||||
#[fail(display = "bad schema assertion: {}", _0)]
|
||||
BadSchemaAssertion(String),
|
||||
|
||||
/// An ident->entid mapping failed.
|
||||
#[fail(display = "no entid found for ident: {}", _0)]
|
||||
UnrecognizedIdent(String),
|
||||
|
||||
/// An entid->ident mapping failed.
|
||||
/// We also use this error if you try to transact an entid that we didn't allocate,
|
||||
/// in part because we blow the stack in error_chain if we define a new enum!
|
||||
#[fail(display = "unrecognized or no ident found for entid: {}", _0)]
|
||||
UnrecognizedEntid(Entid),
|
||||
|
||||
#[fail(display = "unknown attribute for entid: {}", _0)]
|
||||
UnknownAttribute(Entid),
|
||||
|
||||
#[fail(display = "cannot reverse-cache non-unique attribute: {}", _0)]
|
||||
CannotCacheNonUniqueAttributeInReverse(Entid),
|
||||
|
||||
#[fail(display = "schema alteration failed: {}", _0)]
|
||||
SchemaAlterationFailed(String),
|
||||
|
||||
/// A transaction tried to violate a constraint of the schema of the Mentat store.
|
||||
#[fail(display = "schema constraint violation: {}", _0)]
|
||||
SchemaConstraintViolation(SchemaConstraintViolation),
|
||||
|
||||
/// The transaction was malformed in some way (that was not recognized at parse time; for
|
||||
/// example, in a way that is schema-dependent).
|
||||
#[fail(display = "transaction input error: {}", _0)]
|
||||
InputError(InputError),
|
||||
|
||||
#[fail(display = "Cannot transact a fulltext assertion with a typed value that is not :db/valueType :db.type/string")]
|
||||
WrongTypeValueForFtsAssertion,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DbSqlError {
|
||||
inner: Context<DbSqlErrorKind>,
|
||||
}
|
||||
|
||||
impl Fail for DbSqlError {
|
||||
fn cause(&self) -> Option<&Fail> {
|
||||
self.inner.cause()
|
||||
}
|
||||
|
||||
foreign_links {
|
||||
Rusqlite(rusqlite::Error);
|
||||
}
|
||||
|
||||
errors {
|
||||
/// We're just not done yet. Message that the feature is recognized but not yet
|
||||
/// implemented.
|
||||
NotYetImplemented(t: String) {
|
||||
description("not yet implemented")
|
||||
display("not yet implemented: {}", t)
|
||||
}
|
||||
|
||||
/// We've been given a value that isn't the correct Mentat type.
|
||||
BadValuePair(value: String, value_type: ValueType) {
|
||||
description("value is not the expected Mentat value type")
|
||||
display("value '{}' is not the expected Mentat value type {:?}", value, value_type)
|
||||
}
|
||||
|
||||
/// We've got corrupt data in the SQL store: a value and value_type_tag don't line up.
|
||||
BadSQLValuePair(value: rusqlite::types::Value, value_type_tag: i32) {
|
||||
description("bad SQL (value_type_tag, value) pair")
|
||||
display("bad SQL (value_type_tag, value) pair: ({}, {:?})", value_type_tag, value.data_type())
|
||||
}
|
||||
|
||||
// /// The SQLite store user_version isn't recognized. This could be an old version of Mentat
|
||||
// /// trying to open a newer version SQLite store; or it could be a corrupt file; or ...
|
||||
// BadSQLiteStoreVersion(version: i32) {
|
||||
// description("bad SQL store user_version")
|
||||
// display("bad SQL store user_version: {}", version)
|
||||
// }
|
||||
|
||||
/// A bootstrap definition couldn't be parsed or installed. This is a programmer error, not
|
||||
/// a runtime error.
|
||||
BadBootstrapDefinition(t: String) {
|
||||
description("bad bootstrap definition")
|
||||
display("bad bootstrap definition: {}", t)
|
||||
}
|
||||
|
||||
/// A schema assertion couldn't be parsed.
|
||||
BadSchemaAssertion(t: String) {
|
||||
description("bad schema assertion")
|
||||
display("bad schema assertion: {}", t)
|
||||
}
|
||||
|
||||
/// An ident->entid mapping failed.
|
||||
UnrecognizedIdent(ident: String) {
|
||||
description("no entid found for ident")
|
||||
display("no entid found for ident: {}", ident)
|
||||
}
|
||||
|
||||
/// An entid->ident mapping failed.
|
||||
/// We also use this error if you try to transact an entid that we didn't allocate,
|
||||
/// in part because we blow the stack in error_chain if we define a new enum!
|
||||
UnrecognizedEntid(entid: Entid) {
|
||||
description("unrecognized or no ident found for entid")
|
||||
display("unrecognized or no ident found for entid: {}", entid)
|
||||
}
|
||||
|
||||
UnknownAttribute(attr: Entid) {
|
||||
description("unknown attribute")
|
||||
display("unknown attribute for entid: {}", attr)
|
||||
}
|
||||
|
||||
CannotCacheNonUniqueAttributeInReverse(attr: Entid) {
|
||||
description("cannot reverse-cache non-unique attribute")
|
||||
display("cannot reverse-cache non-unique attribute: {}", attr)
|
||||
}
|
||||
|
||||
SchemaAlterationFailed(t: String) {
|
||||
description("schema alteration failed")
|
||||
display("schema alteration failed: {}", t)
|
||||
}
|
||||
|
||||
/// A transaction tried to violate a constraint of the schema of the Mentat store.
|
||||
SchemaConstraintViolation(violation: SchemaConstraintViolation) {
|
||||
description("schema constraint violation")
|
||||
display("schema constraint violation: {}", violation)
|
||||
}
|
||||
|
||||
/// The transaction was malformed in some way (that was not recognized at parse time; for
|
||||
/// example, in a way that is schema-dependent).
|
||||
InputError(error: InputError) {
|
||||
description("transaction input error")
|
||||
display("transaction input error: {}", error)
|
||||
}
|
||||
fn backtrace(&self) -> Option<&Backtrace> {
|
||||
self.inner.backtrace()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for DbSqlError {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
::std::fmt::Display::fmt(&self.inner, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl DbSqlError {
|
||||
pub fn kind(&self) -> DbSqlErrorKind {
|
||||
*self.inner.get_context()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DbSqlErrorKind> for DbSqlError {
|
||||
fn from(kind: DbSqlErrorKind) -> DbSqlError {
|
||||
DbSqlError { inner: Context::new(kind) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Context<DbSqlErrorKind>> for DbSqlError {
|
||||
fn from(inner: Context<DbSqlErrorKind>) -> DbSqlError {
|
||||
DbSqlError { inner: inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
|
||||
pub enum DbSqlErrorKind {
|
||||
#[fail(display = "Could not set_user_version")]
|
||||
CouldNotSetVersionPragma,
|
||||
|
||||
#[fail(display = "Could not get_user_version")]
|
||||
CouldNotGetVersionPragma,
|
||||
|
||||
#[fail(display = "Could not search!")]
|
||||
CouldNotSearch,
|
||||
|
||||
#[fail(display = "Could not insert transaction: failed to add datoms not already present")]
|
||||
TxInsertFailedToAddMissingDatoms,
|
||||
|
||||
#[fail(display = "Could not insert transaction: failed to retract datoms already present")]
|
||||
TxInsertFailedToRetractDatoms,
|
||||
|
||||
#[fail(display = "Could not update datoms: failed to retract datoms already present")]
|
||||
DatomsUpdateFailedToRetract,
|
||||
|
||||
#[fail(display = "Could not update datoms: failed to add datoms not already present")]
|
||||
DatomsUpdateFailedToAdd,
|
||||
|
||||
#[fail(display = "Failed to create temporary tables")]
|
||||
FailedToCreateTempTables,
|
||||
|
||||
#[fail(display = "Could not insert non-fts one statements into temporary search table!")]
|
||||
NonFtsInsertionIntoTempSearchTableFailed,
|
||||
|
||||
#[fail(display = "Could not insert fts values into fts table!")]
|
||||
FtsInsertionFailed,
|
||||
|
||||
#[fail(display = "Could not insert FTS statements into temporary search table!")]
|
||||
FtsInsertionIntoTempSearchTableFailed,
|
||||
|
||||
#[fail(display = "Could not drop FTS search ids!")]
|
||||
FtsFailedToDropSearchIds,
|
||||
|
||||
#[fail(display = "Could not update partition map")]
|
||||
FailedToUpdatePartitionMap,
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ use edn::{
|
|||
|
||||
use errors;
|
||||
use errors::{
|
||||
ErrorKind,
|
||||
DbError,
|
||||
Result,
|
||||
};
|
||||
use schema::{
|
||||
|
@ -69,7 +69,7 @@ impl TransactableValue for ValueAndSpan {
|
|||
Ok(EntityPlace::Entid(entities::Entid::Ident(v)))
|
||||
} else {
|
||||
// We only allow namespaced idents.
|
||||
bail!(ErrorKind::InputError(errors::InputError::BadEntityPlace))
|
||||
bail!(DbError::InputError(errors::InputError::BadEntityPlace))
|
||||
}
|
||||
},
|
||||
Text(v) => Ok(EntityPlace::TempId(TempId::External(v))),
|
||||
|
@ -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::TempId(_) |
|
||||
EntityPlace::TxFunction(_) |
|
||||
EntityPlace::LookupRef(_) => bail!(ErrorKind::InputError(errors::InputError::BadEntityPlace)),
|
||||
EntityPlace::LookupRef(_) => bail!(DbError::InputError(errors::InputError::BadEntityPlace)),
|
||||
}
|
||||
},
|
||||
_ => bail!(ErrorKind::InputError(errors::InputError::BadEntityPlace)),
|
||||
_ => bail!(DbError::InputError(errors::InputError::BadEntityPlace)),
|
||||
}
|
||||
},
|
||||
Nil |
|
||||
|
@ -102,7 +102,7 @@ impl TransactableValue for ValueAndSpan {
|
|||
NamespacedSymbol(_) |
|
||||
Vector(_) |
|
||||
Set(_) |
|
||||
Map(_) => bail!(ErrorKind::InputError(errors::InputError::BadEntityPlace)),
|
||||
Map(_) => bail!(DbError::InputError(errors::InputError::BadEntityPlace)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +114,7 @@ impl TransactableValue for ValueAndSpan {
|
|||
impl TransactableValue for TypedValue {
|
||||
fn into_typed_value(self, _schema: &Schema, value_type: ValueType) -> Result<TypedValue> {
|
||||
if self.value_type() != value_type {
|
||||
bail!(ErrorKind::BadValuePair(format!("{:?}", self), value_type));
|
||||
bail!(DbError::BadValuePair(format!("{:?}", self), value_type));
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ impl TransactableValue for TypedValue {
|
|||
TypedValue::Long(_) |
|
||||
TypedValue::Double(_) |
|
||||
TypedValue::Instant(_) |
|
||||
TypedValue::Uuid(_) => bail!(ErrorKind::InputError(errors::InputError::BadEntityPlace)),
|
||||
TypedValue::Uuid(_) => bail!(DbError::InputError(errors::InputError::BadEntityPlace)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,7 +199,7 @@ pub fn replace_lookup_ref<T, U>(lookup_map: &AVMap, desired_or: Either<T, Lookup
|
|||
LookupRefOrTempId::LookupRef(av) => lookup_map.get(&*av)
|
||||
.map(|x| lift(*x)).map(Left)
|
||||
// XXX TODO: fix this error kind!
|
||||
.ok_or_else(|| ErrorKind::UnrecognizedIdent(format!("couldn't lookup [a v]: {:?}", (*av).clone())).into()),
|
||||
.ok_or_else(|| DbError::UnrecognizedIdent(format!("couldn't lookup [a v]: {:?}", (*av).clone())).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,8 @@
|
|||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
// Oh, error_chain.
|
||||
#![recursion_limit="128"]
|
||||
|
||||
#[macro_use] extern crate error_chain;
|
||||
extern crate failure;
|
||||
#[macro_use] extern crate failure_derive;
|
||||
extern crate indexmap;
|
||||
extern crate itertools;
|
||||
#[macro_use] extern crate lazy_static;
|
||||
|
@ -31,7 +29,12 @@ use std::iter::repeat;
|
|||
|
||||
use itertools::Itertools;
|
||||
|
||||
pub use errors::{Error, ErrorKind, ResultExt, Result};
|
||||
pub use errors::{
|
||||
DbError,
|
||||
Result,
|
||||
SchemaConstraintViolation,
|
||||
};
|
||||
#[macro_use] pub mod errors;
|
||||
|
||||
mod add_retract_alter_set;
|
||||
pub mod cache;
|
||||
|
@ -39,7 +42,6 @@ pub mod db;
|
|||
mod bootstrap;
|
||||
pub mod debug;
|
||||
pub mod entids;
|
||||
pub mod errors;
|
||||
pub mod internal_types; // pub because we need them for building entities programmatically.
|
||||
mod metadata;
|
||||
mod schema;
|
||||
|
@ -114,8 +116,7 @@ pub fn to_namespaced_keyword(s: &str) -> Result<symbols::Keyword> {
|
|||
_ => None,
|
||||
};
|
||||
|
||||
// TODO Use custom ErrorKind https://github.com/brson/error-chain/issues/117
|
||||
nsk.ok_or(ErrorKind::NotYetImplemented(format!("InvalidKeyword: {}", s)).into())
|
||||
nsk.ok_or(DbError::NotYetImplemented(format!("InvalidKeyword: {}", s)).into())
|
||||
}
|
||||
|
||||
/// Prepare an SQL `VALUES` block, like (?, ?, ?), (?, ?, ?).
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
//!
|
||||
//! This module recognizes, validates, applies, and reports on these mutations.
|
||||
|
||||
use failure::ResultExt;
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::collections::btree_map::Entry;
|
||||
|
||||
|
@ -33,9 +35,8 @@ use add_retract_alter_set::{
|
|||
use edn::symbols;
|
||||
use entids;
|
||||
use errors::{
|
||||
ErrorKind,
|
||||
DbError,
|
||||
Result,
|
||||
ResultExt,
|
||||
};
|
||||
use mentat_core::{
|
||||
attribute,
|
||||
|
@ -131,7 +132,7 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
|||
builder.component(false);
|
||||
},
|
||||
v => {
|
||||
bail!(ErrorKind::BadSchemaAssertion(format!("Attempted to retract :db/isComponent with the wrong value {:?}.", v)));
|
||||
bail!(DbError::BadSchemaAssertion(format!("Attempted to retract :db/isComponent with the wrong value {:?}.", v)));
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -146,15 +147,15 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
|||
builder.non_unique();
|
||||
},
|
||||
v => {
|
||||
bail!(ErrorKind::BadSchemaAssertion(format!("Attempted to retract :db/unique with the wrong value {}.", v)));
|
||||
bail!(DbError::BadSchemaAssertion(format!("Attempted to retract :db/unique with the wrong value {}.", v)));
|
||||
},
|
||||
}
|
||||
},
|
||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [:db/retract _ :db/unique :db.unique/_] but got [:db/retract {} :db/unique {:?}]", entid, value)))
|
||||
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [:db/retract _ :db/unique :db.unique/_] but got [:db/retract {} :db/unique {:?}]", entid, value)))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
bail!(ErrorKind::BadSchemaAssertion(format!("Retracting attribute {} for entity {} not permitted.", attr, entid)));
|
||||
bail!(DbError::BadSchemaAssertion(format!("Retracting attribute {} for entity {} not permitted.", attr, entid)));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +169,7 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
|||
entids::DB_DOC => {
|
||||
match *value {
|
||||
TypedValue::String(_) => {},
|
||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/doc \"string value\"] but got [... :db/doc {:?}] for entid {} and attribute {}", value, entid, attr)))
|
||||
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/doc \"string value\"] but got [... :db/doc {:?}] for entid {} and attribute {}", value, entid, attr)))
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -182,7 +183,7 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
|||
TypedValue::Ref(entids::DB_TYPE_REF) => { builder.value_type(ValueType::Ref); },
|
||||
TypedValue::Ref(entids::DB_TYPE_STRING) => { builder.value_type(ValueType::String); },
|
||||
TypedValue::Ref(entids::DB_TYPE_UUID) => { builder.value_type(ValueType::Uuid); },
|
||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/valueType :db.type/*] but got [... :db/valueType {:?}] for entid {} and attribute {}", value, entid, attr)))
|
||||
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/valueType :db.type/*] but got [... :db/valueType {:?}] for entid {} and attribute {}", value, entid, attr)))
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -190,7 +191,7 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
|||
match *value {
|
||||
TypedValue::Ref(entids::DB_CARDINALITY_MANY) => { builder.multival(true); },
|
||||
TypedValue::Ref(entids::DB_CARDINALITY_ONE) => { builder.multival(false); },
|
||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/cardinality :db.cardinality/many|:db.cardinality/one] but got [... :db/cardinality {:?}]", value)))
|
||||
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/cardinality :db.cardinality/many|:db.cardinality/one] but got [... :db/cardinality {:?}]", value)))
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -198,40 +199,40 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
|||
match *value {
|
||||
TypedValue::Ref(entids::DB_UNIQUE_VALUE) => { builder.unique(attribute::Unique::Value); },
|
||||
TypedValue::Ref(entids::DB_UNIQUE_IDENTITY) => { builder.unique(attribute::Unique::Identity); },
|
||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/unique :db.unique/value|:db.unique/identity] but got [... :db/unique {:?}]", value)))
|
||||
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/unique :db.unique/value|:db.unique/identity] but got [... :db/unique {:?}]", value)))
|
||||
}
|
||||
},
|
||||
|
||||
entids::DB_INDEX => {
|
||||
match *value {
|
||||
TypedValue::Boolean(x) => { builder.index(x); },
|
||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/index true|false] but got [... :db/index {:?}]", value)))
|
||||
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/index true|false] but got [... :db/index {:?}]", value)))
|
||||
}
|
||||
},
|
||||
|
||||
entids::DB_FULLTEXT => {
|
||||
match *value {
|
||||
TypedValue::Boolean(x) => { builder.fulltext(x); },
|
||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/fulltext true|false] but got [... :db/fulltext {:?}]", value)))
|
||||
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/fulltext true|false] but got [... :db/fulltext {:?}]", value)))
|
||||
}
|
||||
},
|
||||
|
||||
entids::DB_IS_COMPONENT => {
|
||||
match *value {
|
||||
TypedValue::Boolean(x) => { builder.component(x); },
|
||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/isComponent true|false] but got [... :db/isComponent {:?}]", value)))
|
||||
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/isComponent true|false] but got [... :db/isComponent {:?}]", value)))
|
||||
}
|
||||
},
|
||||
|
||||
entids::DB_NO_HISTORY => {
|
||||
match *value {
|
||||
TypedValue::Boolean(x) => { builder.no_history(x); },
|
||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/noHistory true|false] but got [... :db/noHistory {:?}]", value)))
|
||||
_ => bail!(DbError::BadSchemaAssertion(format!("Expected [... :db/noHistory true|false] but got [... :db/noHistory {:?}]", value)))
|
||||
}
|
||||
},
|
||||
|
||||
_ => {
|
||||
bail!(ErrorKind::BadSchemaAssertion(format!("Do not recognize attribute {} for entid {}", attr, entid)))
|
||||
bail!(DbError::BadSchemaAssertion(format!("Do not recognize attribute {} for entid {}", attr, entid)))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -243,8 +244,7 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
|||
match attribute_map.entry(entid) {
|
||||
Entry::Vacant(entry) => {
|
||||
// Validate once…
|
||||
builder.validate_install_attribute()
|
||||
.chain_err(|| ErrorKind::BadSchemaAssertion(format!("Schema alteration for new attribute with entid {} is not valid", entid)))?;
|
||||
builder.validate_install_attribute().context(DbError::BadSchemaAssertion(format!("Schema alteration for new attribute with entid {} is not valid", entid)))?;
|
||||
|
||||
// … and twice, now we have the Attribute.
|
||||
let a = builder.build();
|
||||
|
@ -254,8 +254,7 @@ pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut Attribu
|
|||
},
|
||||
|
||||
Entry::Occupied(mut entry) => {
|
||||
builder.validate_alter_attribute()
|
||||
.chain_err(|| ErrorKind::BadSchemaAssertion(format!("Schema alteration for existing attribute with entid {} is not valid", entid)))?;
|
||||
builder.validate_alter_attribute().context(DbError::BadSchemaAssertion(format!("Schema alteration for existing attribute with entid {} is not valid", entid)))?;
|
||||
let mutations = builder.mutate(entry.get_mut());
|
||||
attributes_altered.insert(entid, mutations);
|
||||
},
|
||||
|
|
|
@ -12,7 +12,10 @@
|
|||
|
||||
use db::TypedSQLValue;
|
||||
use edn;
|
||||
use errors::{ErrorKind, Result};
|
||||
use errors::{
|
||||
DbError,
|
||||
Result,
|
||||
};
|
||||
use edn::symbols;
|
||||
use mentat_core::{
|
||||
attribute,
|
||||
|
@ -39,19 +42,19 @@ pub trait AttributeValidation {
|
|||
impl AttributeValidation for Attribute {
|
||||
fn validate<F>(&self, ident: F) -> Result<()> where F: Fn() -> String {
|
||||
if self.unique == Some(attribute::Unique::Value) && !self.index {
|
||||
bail!(ErrorKind::BadSchemaAssertion(format!(":db/unique :db/unique_value without :db/index true for entid: {}", ident())))
|
||||
bail!(DbError::BadSchemaAssertion(format!(":db/unique :db/unique_value without :db/index true for entid: {}", ident())))
|
||||
}
|
||||
if self.unique == Some(attribute::Unique::Identity) && !self.index {
|
||||
bail!(ErrorKind::BadSchemaAssertion(format!(":db/unique :db/unique_identity without :db/index true for entid: {}", ident())))
|
||||
bail!(DbError::BadSchemaAssertion(format!(":db/unique :db/unique_identity without :db/index true for entid: {}", ident())))
|
||||
}
|
||||
if self.fulltext && self.value_type != ValueType::String {
|
||||
bail!(ErrorKind::BadSchemaAssertion(format!(":db/fulltext true without :db/valueType :db.type/string for entid: {}", ident())))
|
||||
bail!(DbError::BadSchemaAssertion(format!(":db/fulltext true without :db/valueType :db.type/string for entid: {}", ident())))
|
||||
}
|
||||
if self.fulltext && !self.index {
|
||||
bail!(ErrorKind::BadSchemaAssertion(format!(":db/fulltext true without :db/index true for entid: {}", ident())))
|
||||
bail!(DbError::BadSchemaAssertion(format!(":db/fulltext true without :db/index true for entid: {}", ident())))
|
||||
}
|
||||
if self.component && self.value_type != ValueType::Ref {
|
||||
bail!(ErrorKind::BadSchemaAssertion(format!(":db/isComponent true without :db/valueType :db.type/ref for entid: {}", ident())))
|
||||
bail!(DbError::BadSchemaAssertion(format!(":db/isComponent true without :db/valueType :db.type/ref for entid: {}", ident())))
|
||||
}
|
||||
// TODO: consider warning if we have :db/index true for :db/valueType :db.type/string,
|
||||
// since this may be inefficient. More generally, we should try to drive complex
|
||||
|
@ -150,17 +153,17 @@ impl AttributeBuilder {
|
|||
|
||||
pub fn validate_install_attribute(&self) -> Result<()> {
|
||||
if self.value_type.is_none() {
|
||||
bail!(ErrorKind::BadSchemaAssertion("Schema attribute for new attribute does not set :db/valueType".into()));
|
||||
bail!(DbError::BadSchemaAssertion("Schema attribute for new attribute does not set :db/valueType".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn validate_alter_attribute(&self) -> Result<()> {
|
||||
if self.value_type.is_some() {
|
||||
bail!(ErrorKind::BadSchemaAssertion("Schema alteration must not set :db/valueType".into()));
|
||||
bail!(DbError::BadSchemaAssertion("Schema alteration must not set :db/valueType".into()));
|
||||
}
|
||||
if self.fulltext.is_some() {
|
||||
bail!(ErrorKind::BadSchemaAssertion("Schema alteration must not set :db/fulltext".into()));
|
||||
bail!(DbError::BadSchemaAssertion("Schema alteration must not set :db/fulltext".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -247,15 +250,15 @@ pub trait SchemaBuilding {
|
|||
|
||||
impl SchemaBuilding for Schema {
|
||||
fn require_ident(&self, entid: Entid) -> Result<&symbols::Keyword> {
|
||||
self.get_ident(entid).ok_or(ErrorKind::UnrecognizedEntid(entid).into())
|
||||
self.get_ident(entid).ok_or(DbError::UnrecognizedEntid(entid).into())
|
||||
}
|
||||
|
||||
fn require_entid(&self, ident: &symbols::Keyword) -> Result<KnownEntid> {
|
||||
self.get_entid(&ident).ok_or(ErrorKind::UnrecognizedIdent(ident.to_string()).into())
|
||||
self.get_entid(&ident).ok_or(DbError::UnrecognizedIdent(ident.to_string()).into())
|
||||
}
|
||||
|
||||
fn require_attribute_for_entid(&self, entid: Entid) -> Result<&Attribute> {
|
||||
self.attribute_for_entid(entid).ok_or(ErrorKind::UnrecognizedEntid(entid).into())
|
||||
self.attribute_for_entid(entid).ok_or(DbError::UnrecognizedEntid(entid).into())
|
||||
}
|
||||
|
||||
/// Create a valid `Schema` from the constituent maps.
|
||||
|
@ -271,8 +274,8 @@ impl SchemaBuilding for Schema {
|
|||
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 ident: i64 = *ident_map.get(&symbolic_ident).ok_or(ErrorKind::UnrecognizedIdent(symbolic_ident.to_string()))?;
|
||||
let attr: i64 = *ident_map.get(&symbolic_attr).ok_or(ErrorKind::UnrecognizedIdent(symbolic_attr.to_string()))?;
|
||||
let ident: i64 = *ident_map.get(&symbolic_ident).ok_or(DbError::UnrecognizedIdent(symbolic_ident.to_string()))?;
|
||||
let attr: i64 = *ident_map.get(&symbolic_attr).ok_or(DbError::UnrecognizedIdent(symbolic_attr.to_string()))?;
|
||||
Ok((ident, attr, value))
|
||||
}).collect();
|
||||
|
||||
|
@ -305,7 +308,7 @@ impl SchemaTypeChecking for Schema {
|
|||
// wrapper function.
|
||||
match TypedValue::from_edn_value(&value.clone().without_spans()) {
|
||||
// We don't recognize this EDN at all. Get out!
|
||||
None => bail!(ErrorKind::BadValuePair(format!("{}", value), value_type)),
|
||||
None => bail!(DbError::BadValuePair(format!("{}", value), value_type)),
|
||||
Some(typed_value) => match (value_type, typed_value) {
|
||||
// Most types don't coerce at all.
|
||||
(ValueType::Boolean, tv @ TypedValue::Boolean(_)) => Ok(tv),
|
||||
|
@ -331,7 +334,7 @@ impl SchemaTypeChecking for Schema {
|
|||
(vt @ ValueType::Instant, _) |
|
||||
(vt @ ValueType::Keyword, _) |
|
||||
(vt @ ValueType::Ref, _)
|
||||
=> bail!(ErrorKind::BadValuePair(format!("{}", value), vt)),
|
||||
=> bail!(DbError::BadValuePair(format!("{}", value), vt)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -343,7 +346,6 @@ impl SchemaTypeChecking for Schema {
|
|||
mod test {
|
||||