Store Idents as NamespacedKeywords, rather than Strings. Fixes #291. (#300)

r=ncalexander
This commit is contained in:
Jordan Santell 2017-02-17 13:55:36 -08:00 committed by GitHub
parent ec2bbb8e83
commit a59f9583ac
9 changed files with 145 additions and 99 deletions

View file

@ -177,12 +177,11 @@ impl Default for Attribute {
} }
} }
/// Map `String` idents (`:db/ident`) to positive integer entids (`1`). /// Map `NamespacedKeyword` idents (`:db/ident`) to positive integer entids (`1`).
/// TODO: these should all be parsed into NamespacedKeywords on entry. #291. pub type IdentMap = BTreeMap<NamespacedKeyword, Entid>;
pub type IdentMap = BTreeMap<String, Entid>;
/// Map positive integer entids (`1`) to `String` idents (`:db/ident`). /// Map positive integer entids (`1`) to `NamespacedKeyword` idents (`:db/ident`).
pub type EntidMap = BTreeMap<Entid, String>; pub type EntidMap = BTreeMap<Entid, NamespacedKeyword>;
/// Map attribute entids to `Attribute` instances. /// Map attribute entids to `Attribute` instances.
pub type SchemaMap = BTreeMap<Entid, Attribute>; pub type SchemaMap = BTreeMap<Entid, Attribute>;
@ -214,11 +213,11 @@ pub struct Schema {
} }
impl Schema { impl Schema {
pub fn get_ident(&self, x: Entid) -> Option<&String> { pub fn get_ident(&self, x: Entid) -> Option<&NamespacedKeyword> {
self.entid_map.get(&x) self.entid_map.get(&x)
} }
pub fn get_entid(&self, x: &String) -> Option<Entid> { pub fn get_entid(&self, x: &NamespacedKeyword) -> Option<Entid> {
self.ident_map.get(x).map(|x| *x) self.ident_map.get(x).map(|x| *x)
} }
@ -227,8 +226,7 @@ impl Schema {
} }
pub fn attribute_for_ident(&self, ident: &NamespacedKeyword) -> Option<&Attribute> { pub fn attribute_for_ident(&self, ident: &NamespacedKeyword) -> Option<&Attribute> {
let s = ident.to_string(); // TODO: don't do this. #291. self.get_entid(&ident)
self.get_entid(&s)
.and_then(|x| self.attribute_for_entid(x)) .and_then(|x| self.attribute_for_entid(x))
} }
@ -238,7 +236,7 @@ impl Schema {
} }
/// Return true if the provided ident identifies an attribute in this schema. /// Return true if the provided ident identifies an attribute in this schema.
pub fn identifies_attribute(&self, x: &String) -> bool { pub fn identifies_attribute(&self, x: &NamespacedKeyword) -> bool {
self.get_entid(x).map(|e| self.is_attribute(e)).unwrap_or(false) self.get_entid(x).map(|e| self.is_attribute(e)).unwrap_or(false)
} }
} }

View file

@ -14,6 +14,7 @@ use ::{to_namespaced_keyword};
use edn; use edn;
use errors::{ErrorKind, Result}; use errors::{ErrorKind, Result};
use edn::types::Value; use edn::types::Value;
use edn::symbols;
use entids; use entids;
use db::TypedSQLValue; use db::TypedSQLValue;
use mentat_tx::entities::Entity; use mentat_tx::entities::Entity;
@ -33,63 +34,63 @@ use values;
pub const TX0: i64 = 0x10000000; pub const TX0: i64 = 0x10000000;
lazy_static! { lazy_static! {
static ref V1_IDENTS: Vec<(&'static str, i64)> = { static ref V1_IDENTS: Vec<(symbols::NamespacedKeyword, i64)> = {
vec![(":db/ident", entids::DB_IDENT), vec![(ns_keyword!("db", "ident"), entids::DB_IDENT),
(":db.part/db", entids::DB_PART_DB), (ns_keyword!("db.part", "db"), entids::DB_PART_DB),
(":db/txInstant", entids::DB_TX_INSTANT), (ns_keyword!("db", "txInstant"), entids::DB_TX_INSTANT),
(":db.install/partition", entids::DB_INSTALL_PARTITION), (ns_keyword!("db.install", "partition"), entids::DB_INSTALL_PARTITION),
(":db.install/valueType", entids::DB_INSTALL_VALUETYPE), (ns_keyword!("db.install", "valueType"), entids::DB_INSTALL_VALUETYPE),
(":db.install/attribute", entids::DB_INSTALL_ATTRIBUTE), (ns_keyword!("db.install", "attribute"), entids::DB_INSTALL_ATTRIBUTE),
(":db/valueType", entids::DB_VALUE_TYPE), (ns_keyword!("db", "valueType"), entids::DB_VALUE_TYPE),
(":db/cardinality", entids::DB_CARDINALITY), (ns_keyword!("db", "cardinality"), entids::DB_CARDINALITY),
(":db/unique", entids::DB_UNIQUE), (ns_keyword!("db", "unique"), entids::DB_UNIQUE),
(":db/isComponent", entids::DB_IS_COMPONENT), (ns_keyword!("db", "isComponent"), entids::DB_IS_COMPONENT),
(":db/index", entids::DB_INDEX), (ns_keyword!("db", "index"), entids::DB_INDEX),
(":db/fulltext", entids::DB_FULLTEXT), (ns_keyword!("db", "fulltext"), entids::DB_FULLTEXT),
(":db/noHistory", entids::DB_NO_HISTORY), (ns_keyword!("db", "noHistory"), entids::DB_NO_HISTORY),
(":db/add", entids::DB_ADD), (ns_keyword!("db", "add"), entids::DB_ADD),
(":db/retract", entids::DB_RETRACT), (ns_keyword!("db", "retract"), entids::DB_RETRACT),
(":db.part/user", entids::DB_PART_USER), (ns_keyword!("db.part", "user"), entids::DB_PART_USER),
(":db.part/tx", entids::DB_PART_TX), (ns_keyword!("db.part", "tx"), entids::DB_PART_TX),
(":db/excise", entids::DB_EXCISE), (ns_keyword!("db", "excise"), entids::DB_EXCISE),
(":db.excise/attrs", entids::DB_EXCISE_ATTRS), (ns_keyword!("db.excise", "attrs"), entids::DB_EXCISE_ATTRS),
(":db.excise/beforeT", entids::DB_EXCISE_BEFORE_T), (ns_keyword!("db.excise", "beforeT"), entids::DB_EXCISE_BEFORE_T),
(":db.excise/before", entids::DB_EXCISE_BEFORE), (ns_keyword!("db.excise", "before"), entids::DB_EXCISE_BEFORE),
(":db.alter/attribute", entids::DB_ALTER_ATTRIBUTE), (ns_keyword!("db.alter", "attribute"), entids::DB_ALTER_ATTRIBUTE),
(":db.type/ref", entids::DB_TYPE_REF), (ns_keyword!("db.type", "ref"), entids::DB_TYPE_REF),
(":db.type/keyword", entids::DB_TYPE_KEYWORD), (ns_keyword!("db.type", "keyword"), entids::DB_TYPE_KEYWORD),
(":db.type/long", entids::DB_TYPE_LONG), (ns_keyword!("db.type", "long"), entids::DB_TYPE_LONG),
(":db.type/double", entids::DB_TYPE_DOUBLE), (ns_keyword!("db.type", "double"), entids::DB_TYPE_DOUBLE),
(":db.type/string", entids::DB_TYPE_STRING), (ns_keyword!("db.type", "string"), entids::DB_TYPE_STRING),
(":db.type/boolean", entids::DB_TYPE_BOOLEAN), (ns_keyword!("db.type", "boolean"), entids::DB_TYPE_BOOLEAN),
(":db.type/instant", entids::DB_TYPE_INSTANT), (ns_keyword!("db.type", "instant"), entids::DB_TYPE_INSTANT),
(":db.type/bytes", entids::DB_TYPE_BYTES), (ns_keyword!("db.type", "bytes"), entids::DB_TYPE_BYTES),
(":db.cardinality/one", entids::DB_CARDINALITY_ONE), (ns_keyword!("db.cardinality", "one"), entids::DB_CARDINALITY_ONE),
(":db.cardinality/many", entids::DB_CARDINALITY_MANY), (ns_keyword!("db.cardinality", "many"), entids::DB_CARDINALITY_MANY),
(":db.unique/value", entids::DB_UNIQUE_VALUE), (ns_keyword!("db.unique", "value"), entids::DB_UNIQUE_VALUE),
(":db.unique/identity", entids::DB_UNIQUE_IDENTITY), (ns_keyword!("db.unique", "identity"), entids::DB_UNIQUE_IDENTITY),
(":db/doc", entids::DB_DOC), (ns_keyword!("db", "doc"), entids::DB_DOC),
] ]
}; };
static ref V2_IDENTS: Vec<(&'static str, i64)> = { static ref V2_IDENTS: Vec<(symbols::NamespacedKeyword, i64)> = {
[(*V1_IDENTS).clone(), [(*V1_IDENTS).clone(),
vec![(":db.schema/version", entids::DB_SCHEMA_VERSION), vec![(ns_keyword!("db.schema", "version"), entids::DB_SCHEMA_VERSION),
(":db.schema/attribute", entids::DB_SCHEMA_ATTRIBUTE), (ns_keyword!("db.schema", "attribute"), entids::DB_SCHEMA_ATTRIBUTE),
]].concat() ]].concat()
}; };
static ref V1_PARTS: Vec<(&'static str, i64, i64)> = { static ref V1_PARTS: Vec<(symbols::NamespacedKeyword, i64, i64)> = {
vec![(":db.part/db", 0, (1 + V1_IDENTS.len()) as i64), vec![(ns_keyword!("db.part", "db"), 0, (1 + V1_IDENTS.len()) as i64),
(":db.part/user", 0x10000, 0x10000), (ns_keyword!("db.part", "user"), 0x10000, 0x10000),
(":db.part/tx", TX0, TX0), (ns_keyword!("db.part", "tx"), TX0, TX0),
] ]
}; };
static ref V2_PARTS: Vec<(&'static str, i64, i64)> = { static ref V2_PARTS: Vec<(symbols::NamespacedKeyword, i64, i64)> = {
vec![(":db.part/db", 0, (1 + V2_IDENTS.len()) as i64), vec![(ns_keyword!("db.part", "db"), 0, (1 + V2_IDENTS.len()) as i64),
(":db.part/user", 0x10000, 0x10000), (ns_keyword!("db.part", "user"), 0x10000, 0x10000),
(":db.part/tx", TX0, TX0), (ns_keyword!("db.part", "tx"), TX0, TX0),
] ]
}; };
@ -156,36 +157,37 @@ lazy_static! {
} }
/// Convert (ident, entid) pairs into [:db/add IDENT :db/ident IDENT] `Value` instances. /// Convert (ident, entid) pairs into [:db/add IDENT :db/ident IDENT] `Value` instances.
fn idents_to_assertions(idents: &[(&str, i64)]) -> Vec<Value> { fn idents_to_assertions(idents: &[(symbols::NamespacedKeyword, i64)]) -> Vec<Value> {
idents idents
.into_iter() .into_iter()
.map(|&(ident, _)| { .map(|&(ref ident, _)| {
let value = Value::NamespacedKeyword(to_namespaced_keyword(&ident).unwrap()); let value = Value::NamespacedKeyword(ident.clone());
Value::Vector(vec![values::DB_ADD.clone(), value.clone(), values::DB_IDENT.clone(), value.clone()]) Value::Vector(vec![values::DB_ADD.clone(), value.clone(), values::DB_IDENT.clone(), value.clone()])
}) })
.collect() .collect()
} }
/// Convert {:ident {:key :value ...} ...} to vec![(String(:ident), String(:key), TypedValue(:value)), ...]. /// Convert {:ident {:key :value ...} ...} to
/// vec![(symbols::NamespacedKeyword(:ident), symbols::NamespacedKeyword(:key), TypedValue(:value)), ...].
/// ///
/// Such triples are closer to what the transactor will produce when processing /// Such triples are closer to what the transactor will produce when processing
/// :db.install/attribute assertions. /// :db.install/attribute assertions.
fn symbolic_schema_to_triples(ident_map: &IdentMap, symbolic_schema: &Value) -> Result<Vec<(String, String, TypedValue)>> { fn symbolic_schema_to_triples(ident_map: &IdentMap, symbolic_schema: &Value) -> Result<Vec<(symbols::NamespacedKeyword, symbols::NamespacedKeyword, TypedValue)>> {
// Failure here is a coding error, not a runtime error. // Failure here is a coding error, not a runtime error.
let mut triples: Vec<(String, String, TypedValue)> = vec![]; let mut triples: Vec<(symbols::NamespacedKeyword, symbols::NamespacedKeyword, TypedValue)> = vec![];
// TODO: Consider `flat_map` and `map` rather than loop. // TODO: Consider `flat_map` and `map` rather than loop.
match *symbolic_schema { match *symbolic_schema {
Value::Map(ref m) => { Value::Map(ref m) => {
for (ident, mp) in m { for (ident, mp) in m {
let ident = match ident { let ident = match ident {
&Value::NamespacedKeyword(ref ident) => ident.to_string(), &Value::NamespacedKeyword(ref ident) => ident,
_ => bail!(ErrorKind::BadBootstrapDefinition(format!("Expected namespaced keyword for ident but got '{:?}'", ident))) _ => bail!(ErrorKind::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::NamespacedKeyword(ref attr) => attr.to_string(), &Value::NamespacedKeyword(ref attr) => attr,
_ => bail!(ErrorKind::BadBootstrapDefinition(format!("Expected namespaced keyword for attr but got '{:?}'", attr))) _ => bail!(ErrorKind::BadBootstrapDefinition(format!("Expected namespaced keyword for attr but got '{:?}'", attr)))
}; };
@ -198,14 +200,17 @@ fn symbolic_schema_to_triples(ident_map: &IdentMap, symbolic_schema: &Value) ->
// bootstrap symbolic schema, or by representing the initial bootstrap // bootstrap symbolic schema, or by representing the initial bootstrap
// schema directly as Rust data. // schema directly as Rust data.
let typed_value = match TypedValue::from_edn_value(value) { let typed_value = match TypedValue::from_edn_value(value) {
Some(TypedValue::Keyword(ref s)) => TypedValue::Ref(*ident_map.get(s).ok_or(ErrorKind::UnrecognizedIdent(s.clone()))?), Some(TypedValue::Keyword(ref s)) => {
to_namespaced_keyword(s)
.and_then(|ident| ident_map.get(&ident))
.map(|entid| TypedValue::Ref(*entid))
.ok_or(ErrorKind::UnrecognizedIdent(s.clone()))?
},
Some(v) => v, Some(v) => v,
_ => bail!(ErrorKind::BadBootstrapDefinition(format!("Expected Mentat typed value for value but got '{:?}'", value))) _ => bail!(ErrorKind::BadBootstrapDefinition(format!("Expected Mentat typed value for value but got '{:?}'", value)))
}; };
triples.push((ident.clone(), triples.push((ident.clone(), attr.clone(), typed_value));
attr.clone(),
typed_value));
} }
}, },
_ => bail!(ErrorKind::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into())) _ => bail!(ErrorKind::BadBootstrapDefinition("Expected {:db/ident {:db/attr value ...} ...}".into()))
@ -249,13 +254,13 @@ fn symbolic_schema_to_assertions(symbolic_schema: &Value) -> Result<Vec<Value>>
pub fn bootstrap_partition_map() -> PartitionMap { pub fn bootstrap_partition_map() -> PartitionMap {
V2_PARTS[..].iter() V2_PARTS[..].iter()
.map(|&(part, start, index)| (part.to_string(), Partition::new(start, index))) .map(|&(ref part, start, index)| (part.to_string(), Partition::new(start, index)))
.collect() .collect()
} }
pub fn bootstrap_ident_map() -> IdentMap { pub fn bootstrap_ident_map() -> IdentMap {
V2_IDENTS[..].iter() V2_IDENTS[..].iter()
.map(|&(ident, entid)| (ident.to_string(), entid)) .map(|&(ref ident, entid)| (ident.clone(), entid))
.collect() .collect()
} }

View file

@ -25,6 +25,7 @@ use rusqlite::types::{ToSql, ToSqlOutput};
use ::{now, repeat_values, to_namespaced_keyword}; use ::{now, repeat_values, to_namespaced_keyword};
use bootstrap; use bootstrap;
use edn::types::Value; use edn::types::Value;
use edn::symbols;
use mentat_core::{ use mentat_core::{
Attribute, Attribute,
AttributeBitFlags, AttributeBitFlags,
@ -387,7 +388,13 @@ impl TypedSQLValue for TypedValue {
&TypedValue::Long(x) => (Value::Integer(x), ValueType::Long), &TypedValue::Long(x) => (Value::Integer(x), ValueType::Long),
&TypedValue::Double(x) => (Value::Float(x), ValueType::Double), &TypedValue::Double(x) => (Value::Float(x), ValueType::Double),
&TypedValue::String(ref x) => (Value::Text(x.clone()), ValueType::String), &TypedValue::String(ref x) => (Value::Text(x.clone()), ValueType::String),
&TypedValue::Keyword(ref x) => (Value::NamespacedKeyword(to_namespaced_keyword(&x).unwrap()), ValueType::Keyword), &TypedValue::Keyword(ref x) => {
match to_namespaced_keyword(&x) {
Some(x) => (Value::NamespacedKeyword(x), ValueType::Keyword),
// TODO Use custom ErrorKind https://github.com/brson/error-chain/issues/117
None => panic!(ErrorKind::NotYetImplemented(format!("InvalidNamespacedKeyword: {}", x))),
}
},
} }
} }
} }
@ -395,13 +402,16 @@ impl TypedSQLValue for TypedValue {
/// Read the ident map materialized view from the given SQL store. /// Read the ident map materialized view from the given SQL store.
pub fn read_ident_map(conn: &rusqlite::Connection) -> Result<IdentMap> { pub fn read_ident_map(conn: &rusqlite::Connection) -> Result<IdentMap> {
let mut stmt: rusqlite::Statement = conn.prepare("SELECT ident, entid FROM idents")?; let mut stmt: rusqlite::Statement = conn.prepare("SELECT ident, entid FROM idents")?;
let m = stmt.query_and_then(&[], |row| -> Result<(String, Entid)> { let m = stmt.query_and_then(&[], |row| -> Result<(symbols::NamespacedKeyword, Entid)> {
Ok((row.get(0), row.get(1))) let ident: String = row.get(0);
to_namespaced_keyword(&ident)
.map(|i| (i, row.get(1)))
// TODO Use custom ErrorKind https://github.com/brson/error-chain/issues/117
.ok_or(ErrorKind::NotYetImplemented(format!("InvalidNamespacedKeyword: {}", ident.clone())).into())
})?.collect(); })?.collect();
m m
} }
/// Read the partition map materialized view from the given SQL store. /// Read the partition map materialized view from the given SQL store.
pub fn read_partition_map(conn: &rusqlite::Connection) -> Result<PartitionMap> { pub fn read_partition_map(conn: &rusqlite::Connection) -> Result<PartitionMap> {
let mut stmt: rusqlite::Statement = conn.prepare("SELECT part, start, idx FROM parts")?; let mut stmt: rusqlite::Statement = conn.prepare("SELECT part, start, idx FROM parts")?;
@ -414,7 +424,7 @@ pub fn read_partition_map(conn: &rusqlite::Connection) -> Result<PartitionMap> {
/// Read the schema materialized view from the given SQL store. /// Read the schema materialized view from the given SQL store.
pub fn read_schema(conn: &rusqlite::Connection, ident_map: &IdentMap) -> Result<Schema> { pub fn read_schema(conn: &rusqlite::Connection, ident_map: &IdentMap) -> Result<Schema> {
let mut stmt: rusqlite::Statement = conn.prepare("SELECT ident, attr, value, value_type_tag FROM schema")?; let mut stmt: rusqlite::Statement = conn.prepare("SELECT ident, attr, value, value_type_tag FROM schema")?;
let r: Result<Vec<(String, String, TypedValue)>> = stmt.query_and_then(&[], |row| { let r: Result<Vec<(symbols::NamespacedKeyword, symbols::NamespacedKeyword, TypedValue)>> = stmt.query_and_then(&[], |row| {
// Each row looks like :db/index|:db/valueType|28|0. Observe that 28|0 represents a // Each row looks like :db/index|:db/valueType|28|0. Observe that 28|0 represents a
// :db.type/ref to entid 28, which needs to be converted to a TypedValue. // :db.type/ref to entid 28, which needs to be converted to a TypedValue.
// TODO: don't use textual ident and attr; just use entids directly. // TODO: don't use textual ident and attr; just use entids directly.
@ -424,7 +434,17 @@ pub fn read_schema(conn: &rusqlite::Connection, ident_map: &IdentMap) -> Result<
let value_type_tag: i32 = row.get_checked(3)?; let value_type_tag: i32 = row.get_checked(3)?;
let typed_value = TypedValue::from_sql_value_pair(v, value_type_tag)?; let typed_value = TypedValue::from_sql_value_pair(v, value_type_tag)?;
Ok((symbolic_ident, symbolic_attr, typed_value)) let ident = to_namespaced_keyword(&symbolic_ident);
let attr = to_namespaced_keyword(&symbolic_attr);
match (ident, attr, typed_value) {
(Some(ident), Some(attr), typed_value) => Ok((ident, attr, typed_value)),
(None, _, _) =>
// TODO Use custom ErrorKind https://github.com/brson/error-chain/issues/117
Err(ErrorKind::NotYetImplemented(format!("InvalidNamespacedKeyword: {}", &symbolic_ident)).into()),
(_, None, _) =>
// TODO Use custom ErrorKind https://github.com/brson/error-chain/issues/117
Err(ErrorKind::NotYetImplemented(format!("InvalidNamespacedKeyword: {}", &symbolic_attr)).into()),
}
})?.collect(); })?.collect();
r.and_then(|triples| Schema::from_ident_map_and_triples(ident_map.clone(), triples)) r.and_then(|triples| Schema::from_ident_map_and_triples(ident_map.clone(), triples))
@ -467,7 +487,13 @@ impl DB {
(&ValueType::Keyword, tv @ TypedValue::Keyword(_)) => Ok(tv), (&ValueType::Keyword, tv @ TypedValue::Keyword(_)) => Ok(tv),
// Ref coerces a little: we interpret some things depending on the schema as a Ref. // Ref coerces a little: we interpret some things depending on the schema as a Ref.
(&ValueType::Ref, TypedValue::Long(x)) => Ok(TypedValue::Ref(x)), (&ValueType::Ref, TypedValue::Long(x)) => Ok(TypedValue::Ref(x)),
(&ValueType::Ref, TypedValue::Keyword(ref x)) => self.schema.require_entid(&x.to_string()).map(|entid| TypedValue::Ref(entid)), (&ValueType::Ref, TypedValue::Keyword(ref x)) => {
match to_namespaced_keyword(x) {
Some(x) => self.schema.require_entid(&x).map(|entid| TypedValue::Ref(entid)),
// TODO Use custom ErrorKind https://github.com/brson/error-chain/issues/117
None => bail!(ErrorKind::NotYetImplemented(format!("InvalidNamespacedKeyword: {}", x))),
}
}
// Otherwise, we have a type mismatch. // Otherwise, we have a type mismatch.
(value_type, _) => bail!(ErrorKind::BadEDNValuePair(value.clone(), value_type.clone())), (value_type, _) => bail!(ErrorKind::BadEDNValuePair(value.clone(), value_type.clone())),
} }

View file

@ -107,7 +107,7 @@ impl Transactions {
/// Convert a numeric entid to an ident `Entid` if possible, otherwise a numeric `Entid`. /// Convert a numeric entid to an ident `Entid` if possible, otherwise a numeric `Entid`.
fn to_entid(db: &DB, entid: i64) -> Entid { fn to_entid(db: &DB, entid: i64) -> Entid {
db.schema.get_ident(entid).and_then(|ident| to_namespaced_keyword(&ident)).map_or(Entid::Entid(entid), Entid::Ident) db.schema.get_ident(entid).map_or(Entid::Entid(entid), |ident| Entid::Ident(ident.clone()))
} }
/// Return the set of datoms in the store, ordered by (e, a, v, tx), but not including any datoms of /// Return the set of datoms in the store, ordered by (e, a, v, tx), but not including any datoms of

View file

@ -18,6 +18,7 @@ extern crate time;
extern crate tabwriter; extern crate tabwriter;
#[macro_use]
extern crate edn; extern crate edn;
extern crate mentat_core; extern crate mentat_core;
extern crate mentat_tx; extern crate mentat_tx;

View file

@ -12,6 +12,7 @@
use entids; use entids;
use errors::*; use errors::*;
use edn::symbols;
use mentat_core::{ use mentat_core::{
Attribute, Attribute,
Entid, Entid,
@ -50,21 +51,21 @@ fn validate_schema_map(entid_map: &EntidMap, schema_map: &SchemaMap) -> Result<(
} }
pub trait SchemaBuilding { pub trait SchemaBuilding {
fn require_ident(&self, entid: Entid) -> Result<&String>; fn require_ident(&self, entid: Entid) -> Result<&symbols::NamespacedKeyword>;
fn require_entid(&self, ident: &String) -> Result<Entid>; fn require_entid(&self, ident: &symbols::NamespacedKeyword) -> Result<Entid>;
fn require_attribute_for_entid(&self, entid: Entid) -> Result<&Attribute>; fn require_attribute_for_entid(&self, entid: Entid) -> Result<&Attribute>;
fn from_ident_map_and_schema_map(ident_map: IdentMap, schema_map: SchemaMap) -> Result<Schema>; fn from_ident_map_and_schema_map(ident_map: IdentMap, schema_map: SchemaMap) -> Result<Schema>;
fn from_ident_map_and_triples<U>(ident_map: IdentMap, assertions: U) -> Result<Schema> fn from_ident_map_and_triples<U>(ident_map: IdentMap, assertions: U) -> Result<Schema>
where U: IntoIterator<Item=(String, String, TypedValue)>; where U: IntoIterator<Item=(symbols::NamespacedKeyword, symbols::NamespacedKeyword, TypedValue)>;
} }
impl SchemaBuilding for Schema { impl SchemaBuilding for Schema {
fn require_ident(&self, entid: Entid) -> Result<&String> { fn require_ident(&self, entid: Entid) -> Result<&symbols::NamespacedKeyword> {
self.get_ident(entid).ok_or(ErrorKind::UnrecognizedEntid(entid).into()) self.get_ident(entid).ok_or(ErrorKind::UnrecognizedEntid(entid).into())
} }
fn require_entid(&self, ident: &String) -> Result<Entid> { fn require_entid(&self, ident: &symbols::NamespacedKeyword) -> Result<Entid> {
self.get_entid(&ident).ok_or(ErrorKind::UnrecognizedIdent(ident.clone()).into()) self.get_entid(&ident).ok_or(ErrorKind::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> {
@ -84,13 +85,13 @@ impl SchemaBuilding for Schema {
}) })
} }
/// Turn vec![(String(:ident), String(:key), TypedValue(:value)), ...] into a Mentat `Schema`. /// Turn vec![(NamespacedKeyword(:ident), NamespacedKeyword(:key), TypedValue(:value)), ...] into a Mentat `Schema`.
fn from_ident_map_and_triples<U>(ident_map: IdentMap, assertions: U) -> Result<Schema> fn from_ident_map_and_triples<U>(ident_map: IdentMap, assertions: U) -> Result<Schema>
where U: IntoIterator<Item=(String, String, TypedValue)>{ where U: IntoIterator<Item=(symbols::NamespacedKeyword, symbols::NamespacedKeyword, TypedValue)>{
let mut schema_map = SchemaMap::new(); let mut schema_map = SchemaMap::new();
for (ref symbolic_ident, ref symbolic_attr, ref value) in assertions.into_iter() { for (ref symbolic_ident, ref symbolic_attr, ref value) in assertions.into_iter() {
let ident: i64 = *ident_map.get(symbolic_ident).ok_or(ErrorKind::UnrecognizedIdent(symbolic_ident.clone()))?; 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.clone()))?; let attr: i64 = *ident_map.get(symbolic_attr).ok_or(ErrorKind::UnrecognizedIdent(symbolic_attr.to_string()))?;
let attributes = schema_map.entry(ident).or_insert(Attribute::default()); let attributes = schema_map.entry(ident).or_insert(Attribute::default());
// TODO: improve error messages throughout. // TODO: improve error messages throughout.

View file

@ -144,7 +144,7 @@ impl<'conn> Tx<'conn> {
Entity::AddOrRetract { op, e, a, v } => { Entity::AddOrRetract { op, e, a, v } => {
let a: i64 = match a { let a: i64 = match a {
entmod::Entid::Entid(ref a) => *a, entmod::Entid::Entid(ref a) => *a,
entmod::Entid::Ident(ref a) => self.db.schema.require_entid(&a.to_string())?, entmod::Entid::Ident(ref a) => self.db.schema.require_entid(&a)?,
}; };
let attribute: &Attribute = self.db.schema.require_attribute_for_entid(a)?; let attribute: &Attribute = self.db.schema.require_attribute_for_entid(a)?;
@ -153,7 +153,7 @@ impl<'conn> Tx<'conn> {
entmod::EntidOrLookupRefOrTempId::Entid(e) => { entmod::EntidOrLookupRefOrTempId::Entid(e) => {
let e: i64 = match e { let e: i64 = match e {
entmod::Entid::Entid(ref e) => *e, entmod::Entid::Entid(ref e) => *e,
entmod::Entid::Ident(ref e) => self.db.schema.require_entid(&e.to_string())?, entmod::Entid::Ident(ref e) => self.db.schema.require_entid(&e)?,
}; };
std::result::Result::Ok(e) std::result::Result::Ok(e)
}, },

View file

@ -277,3 +277,18 @@ impl Display for NamespacedKeyword {
write!(f, ":{}/{}", self.namespace, self.name) write!(f, ":{}/{}", self.namespace, self.name)
} }
} }
#[macro_export]
macro_rules! ns_keyword {
($ns: expr, $name: expr) => {{
$crate::NamespacedKeyword::new($ns, $name)
}}
}
#[test]
fn test_ns_keyword_macro() {
assert_eq!(ns_keyword!("test", "name").to_string(),
NamespacedKeyword::new("test", "name").to_string());
assert_eq!(ns_keyword!("ns", "_name").to_string(),
NamespacedKeyword::new("ns", "_name").to_string());
}

View file

@ -262,7 +262,7 @@ impl ConjoiningClauses {
} }
fn entid_for_ident<'s, 'a>(&self, schema: &'s Schema, ident: &'a NamespacedKeyword) -> Option<Entid> { fn entid_for_ident<'s, 'a>(&self, schema: &'s Schema, ident: &'a NamespacedKeyword) -> Option<Entid> {
schema.get_entid(&ident.to_string()) // TODO: #291. schema.get_entid(&ident)
} }
fn table_for_attribute_and_value<'s, 'a>(&self, attribute: &'s Attribute, value: &'a PatternValuePlace) -> Option<DatomsTable> { fn table_for_attribute_and_value<'s, 'a>(&self, attribute: &'s Attribute, value: &'a PatternValuePlace) -> Option<DatomsTable> {
@ -602,9 +602,9 @@ impl ConjoiningClauses {
mod testing { mod testing {
use super::*; use super::*;
fn associate_ident(schema: &mut Schema, i: &str, e: Entid) { fn associate_ident(schema: &mut Schema, i: NamespacedKeyword, e: Entid) {
schema.entid_map.insert(e, i.to_string()); schema.entid_map.insert(e, i.clone());
schema.ident_map.insert(i.to_string(), e); schema.ident_map.insert(i.clone(), e);
} }
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) { fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
@ -632,7 +632,7 @@ mod testing {
let mut cc = ConjoiningClauses::default(); let mut cc = ConjoiningClauses::default();
let mut schema = Schema::default(); let mut schema = Schema::default();
associate_ident(&mut schema, ":foo/bar", 99); associate_ident(&mut schema, NamespacedKeyword::new("foo", "bar"), 99);
cc.apply_pattern(&schema, &Pattern { cc.apply_pattern(&schema, &Pattern {
source: None, source: None,
@ -650,7 +650,7 @@ mod testing {
let mut cc = ConjoiningClauses::default(); let mut cc = ConjoiningClauses::default();
let mut schema = Schema::default(); let mut schema = Schema::default();
associate_ident(&mut schema, ":foo/bar", 99); associate_ident(&mut schema, NamespacedKeyword::new("foo", "bar"), 99);
add_attribute(&mut schema, 99, Attribute { add_attribute(&mut schema, 99, Attribute {
value_type: ValueType::Boolean, value_type: ValueType::Boolean,
..Default::default() ..Default::default()
@ -733,8 +733,8 @@ mod testing {
let mut cc = ConjoiningClauses::default(); let mut cc = ConjoiningClauses::default();
let mut schema = Schema::default(); let mut schema = Schema::default();
associate_ident(&mut schema, ":foo/bar", 99); associate_ident(&mut schema, NamespacedKeyword::new("foo", "bar"), 99);
associate_ident(&mut schema, ":foo/roz", 98); associate_ident(&mut schema, NamespacedKeyword::new("foo", "roz"), 98);
add_attribute(&mut schema, 99, Attribute { add_attribute(&mut schema, 99, Attribute {
value_type: ValueType::Boolean, value_type: ValueType::Boolean,
..Default::default() ..Default::default()