r=ncalexander
This commit is contained in:
parent
ec2bbb8e83
commit
a59f9583ac
9 changed files with 145 additions and 99 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
40
db/src/db.rs
40
db/src/db.rs
|
@ -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())),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
},
|
},
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue