Preliminary work for vocabulary management. r=emily,nalexander
Pre: export AttributeBuilder from mentat_db. Pre: fix module-level comment for tx/src/entities.rs. Pre: rename some `to_` conversions to `into_`. Pre: make AttributeBuilder::unique less verbose. Pre: split out a HasSchema trait to abstract over Schema. Pre: rename SchemaMap/schema_map to AttributeMap/attribute_map. Pre: TypedValue/NamespacedKeyword conversions. Pre: turn Unique and ValueType into TypedValue::Keyword. Pre: export IntoResult. Pre: export NamespacedKeyword from mentat_core. Pre: use intern_set in tx. Pre: add InternSet::len. Pre: comment gardening. Pre: remove inaccurate TODO from TxReport comment.
This commit is contained in:
parent
224570fb45
commit
6797a606b5
22 changed files with 150 additions and 80 deletions
|
@ -33,6 +33,10 @@ impl<T> InternSet<T> where T: Eq + Hash {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.inner.len()
|
||||||
|
}
|
||||||
|
|
||||||
/// Intern a value, providing a ref-counted handle to the interned value.
|
/// Intern a value, providing a ref-counted handle to the interned value.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|
101
core/src/lib.rs
101
core/src/lib.rs
|
@ -31,9 +31,6 @@ use std::rc::Rc;
|
||||||
use enum_set::EnumSet;
|
use enum_set::EnumSet;
|
||||||
|
|
||||||
use self::ordered_float::OrderedFloat;
|
use self::ordered_float::OrderedFloat;
|
||||||
use self::edn::{
|
|
||||||
NamespacedKeyword,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use uuid::Uuid;
|
pub use uuid::Uuid;
|
||||||
|
|
||||||
|
@ -44,6 +41,7 @@ pub use chrono::{
|
||||||
|
|
||||||
pub use edn::{
|
pub use edn::{
|
||||||
FromMicros,
|
FromMicros,
|
||||||
|
NamespacedKeyword,
|
||||||
ToMicros,
|
ToMicros,
|
||||||
Utc,
|
Utc,
|
||||||
};
|
};
|
||||||
|
@ -102,7 +100,33 @@ impl enum_set::CLike for ValueType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValueType {
|
impl ValueType {
|
||||||
pub fn to_edn_value(self) -> edn::Value {
|
pub fn into_keyword(self) -> NamespacedKeyword {
|
||||||
|
NamespacedKeyword::new("db.type", match self {
|
||||||
|
ValueType::Ref => "ref",
|
||||||
|
ValueType::Boolean => "boolean",
|
||||||
|
ValueType::Instant => "instant",
|
||||||
|
ValueType::Long => "long",
|
||||||
|
ValueType::Double => "double",
|
||||||
|
ValueType::String => "string",
|
||||||
|
ValueType::Keyword => "keyword",
|
||||||
|
ValueType::Uuid => "uuid",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_typed_value(self) -> TypedValue {
|
||||||
|
TypedValue::typed_ns_keyword("db.type", match self {
|
||||||
|
ValueType::Ref => "ref",
|
||||||
|
ValueType::Boolean => "boolean",
|
||||||
|
ValueType::Instant => "instant",
|
||||||
|
ValueType::Long => "long",
|
||||||
|
ValueType::Double => "double",
|
||||||
|
ValueType::String => "string",
|
||||||
|
ValueType::Keyword => "keyword",
|
||||||
|
ValueType::Uuid => "uuid",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_edn_value(self) -> edn::Value {
|
||||||
match self {
|
match self {
|
||||||
ValueType::Ref => values::DB_TYPE_REF.clone(),
|
ValueType::Ref => values::DB_TYPE_REF.clone(),
|
||||||
ValueType::Boolean => values::DB_TYPE_BOOLEAN.clone(),
|
ValueType::Boolean => values::DB_TYPE_BOOLEAN.clone(),
|
||||||
|
@ -480,11 +504,23 @@ pub enum AttributeBitFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod attribute {
|
pub mod attribute {
|
||||||
#[derive(Clone,Debug,Eq,Hash,Ord,PartialOrd,PartialEq)]
|
use TypedValue;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
|
||||||
pub enum Unique {
|
pub enum Unique {
|
||||||
Value,
|
Value,
|
||||||
Identity,
|
Identity,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Unique {
|
||||||
|
// This is easier than rejigging DB_UNIQUE_VALUE to not be EDN.
|
||||||
|
pub fn into_typed_value(self) -> TypedValue {
|
||||||
|
match self {
|
||||||
|
Unique::Value => TypedValue::typed_ns_keyword("db.unique", "value"),
|
||||||
|
Unique::Identity => TypedValue::typed_ns_keyword("db.unique", "identity"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Mentat schema attribute has a value type and several other flags determining how assertions
|
/// A Mentat schema attribute has a value type and several other flags determining how assertions
|
||||||
|
@ -561,7 +597,7 @@ impl Attribute {
|
||||||
attribute_map.insert(values::DB_IDENT.clone(), edn::Value::NamespacedKeyword(ident));
|
attribute_map.insert(values::DB_IDENT.clone(), edn::Value::NamespacedKeyword(ident));
|
||||||
}
|
}
|
||||||
|
|
||||||
attribute_map.insert(values::DB_VALUE_TYPE.clone(), self.value_type.to_edn_value());
|
attribute_map.insert(values::DB_VALUE_TYPE.clone(), self.value_type.into_edn_value());
|
||||||
|
|
||||||
attribute_map.insert(values::DB_CARDINALITY.clone(), if self.multival { values::DB_CARDINALITY_MANY.clone() } else { values::DB_CARDINALITY_ONE.clone() });
|
attribute_map.insert(values::DB_CARDINALITY.clone(), if self.multival { values::DB_CARDINALITY_MANY.clone() } else { values::DB_CARDINALITY_ONE.clone() });
|
||||||
|
|
||||||
|
@ -608,7 +644,7 @@ pub type IdentMap = BTreeMap<NamespacedKeyword, Entid>;
|
||||||
pub type EntidMap = BTreeMap<Entid, NamespacedKeyword>;
|
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 AttributeMap = BTreeMap<Entid, Attribute>;
|
||||||
|
|
||||||
/// Represents a Mentat schema.
|
/// Represents a Mentat schema.
|
||||||
///
|
///
|
||||||
|
@ -633,44 +669,59 @@ pub struct Schema {
|
||||||
///
|
///
|
||||||
/// Invariant: key-set is the same as the key-set of `entid_map` (equivalently, the value-set of
|
/// Invariant: key-set is the same as the key-set of `entid_map` (equivalently, the value-set of
|
||||||
/// `ident_map`).
|
/// `ident_map`).
|
||||||
pub schema_map: SchemaMap,
|
pub attribute_map: AttributeMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait HasSchema {
|
||||||
|
fn get_ident(&self, x: Entid) -> Option<&NamespacedKeyword>;
|
||||||
|
fn get_entid(&self, x: &NamespacedKeyword) -> Option<Entid>;
|
||||||
|
fn attribute_for_entid(&self, x: Entid) -> Option<&Attribute>;
|
||||||
|
fn attribute_for_ident(&self, ident: &NamespacedKeyword) -> Option<&Attribute>;
|
||||||
|
|
||||||
|
/// Return true if the provided entid identifies an attribute in this schema.
|
||||||
|
fn is_attribute(&self, x: Entid) -> bool;
|
||||||
|
|
||||||
|
/// Return true if the provided ident identifies an attribute in this schema.
|
||||||
|
fn identifies_attribute(&self, x: &NamespacedKeyword) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Schema {
|
impl Schema {
|
||||||
pub fn get_ident(&self, x: Entid) -> Option<&NamespacedKeyword> {
|
/// Returns an symbolic representation of the schema suitable for applying across Mentat stores.
|
||||||
|
pub fn to_edn_value(&self) -> edn::Value {
|
||||||
|
edn::Value::Vector((&self.attribute_map).iter()
|
||||||
|
.map(|(entid, attribute)|
|
||||||
|
attribute.to_edn_value(self.get_ident(*entid).cloned()))
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasSchema for Schema {
|
||||||
|
fn get_ident(&self, x: Entid) -> Option<&NamespacedKeyword> {
|
||||||
self.entid_map.get(&x)
|
self.entid_map.get(&x)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_entid(&self, x: &NamespacedKeyword) -> Option<Entid> {
|
fn get_entid(&self, x: &NamespacedKeyword) -> Option<Entid> {
|
||||||
self.ident_map.get(x).map(|x| *x)
|
self.ident_map.get(x).map(|x| *x)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attribute_for_entid(&self, x: Entid) -> Option<&Attribute> {
|
fn attribute_for_entid(&self, x: Entid) -> Option<&Attribute> {
|
||||||
self.schema_map.get(&x)
|
self.attribute_map.get(&x)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attribute_for_ident(&self, ident: &NamespacedKeyword) -> Option<&Attribute> {
|
fn attribute_for_ident(&self, ident: &NamespacedKeyword) -> Option<&Attribute> {
|
||||||
self.get_entid(&ident)
|
self.get_entid(&ident)
|
||||||
.and_then(|x| self.attribute_for_entid(x))
|
.and_then(|x| self.attribute_for_entid(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if the provided entid identifies an attribute in this schema.
|
/// Return true if the provided entid identifies an attribute in this schema.
|
||||||
pub fn is_attribute(&self, x: Entid) -> bool {
|
fn is_attribute(&self, x: Entid) -> bool {
|
||||||
self.schema_map.contains_key(&x)
|
self.attribute_map.contains_key(&x)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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: &NamespacedKeyword) -> bool {
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an symbolic representation of the schema suitable for applying across Mentat stores.
|
|
||||||
pub fn to_edn_value(&self) -> edn::Value {
|
|
||||||
edn::Value::Vector((&self.schema_map).iter()
|
|
||||||
.map(|(entid, attribute)|
|
|
||||||
attribute.to_edn_value(self.get_ident(*entid).cloned()))
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -685,7 +736,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
|
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
|
||||||
schema.schema_map.insert(e, a);
|
schema.attribute_map.insert(e, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
20
db/src/db.rs
20
db/src/db.rs
|
@ -44,7 +44,7 @@ use mentat_core::{
|
||||||
FromMicros,
|
FromMicros,
|
||||||
IdentMap,
|
IdentMap,
|
||||||
Schema,
|
Schema,
|
||||||
SchemaMap,
|
AttributeMap,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
ToMicros,
|
ToMicros,
|
||||||
ValueType,
|
ValueType,
|
||||||
|
@ -479,11 +479,11 @@ fn read_ident_map(conn: &rusqlite::Connection) -> Result<IdentMap> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the schema materialized view from the given SQL store.
|
/// Read the schema materialized view from the given SQL store.
|
||||||
fn read_schema_map(conn: &rusqlite::Connection) -> Result<SchemaMap> {
|
fn read_attribute_map(conn: &rusqlite::Connection) -> Result<AttributeMap> {
|
||||||
let entid_triples = read_materialized_view(conn, "schema")?;
|
let entid_triples = read_materialized_view(conn, "schema")?;
|
||||||
let mut schema_map = SchemaMap::default();
|
let mut attribute_map = AttributeMap::default();
|
||||||
metadata::update_schema_map_from_entid_triples(&mut schema_map, entid_triples)?;
|
metadata::update_attribute_map_from_entid_triples(&mut attribute_map, entid_triples)?;
|
||||||
Ok(schema_map)
|
Ok(attribute_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the materialized views from the given SQL store and return a Mentat `DB` for querying and
|
/// Read the materialized views from the given SQL store and return a Mentat `DB` for querying and
|
||||||
|
@ -491,8 +491,8 @@ fn read_schema_map(conn: &rusqlite::Connection) -> Result<SchemaMap> {
|
||||||
pub fn read_db(conn: &rusqlite::Connection) -> Result<DB> {
|
pub fn read_db(conn: &rusqlite::Connection) -> Result<DB> {
|
||||||
let partition_map = read_partition_map(conn)?;
|
let partition_map = read_partition_map(conn)?;
|
||||||
let ident_map = read_ident_map(conn)?;
|
let ident_map = read_ident_map(conn)?;
|
||||||
let schema_map = read_schema_map(conn)?;
|
let attribute_map = read_attribute_map(conn)?;
|
||||||
let schema = Schema::from_ident_map_and_schema_map(ident_map, schema_map)?;
|
let schema = Schema::from_ident_map_and_attribute_map(ident_map, attribute_map)?;
|
||||||
Ok(DB::new(partition_map, schema))
|
Ok(DB::new(partition_map, schema))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1106,6 +1106,8 @@ mod tests {
|
||||||
use debug;
|
use debug;
|
||||||
use edn;
|
use edn;
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
|
HasSchema,
|
||||||
|
Schema,
|
||||||
attribute,
|
attribute,
|
||||||
};
|
};
|
||||||
use mentat_tx_parser;
|
use mentat_tx_parser;
|
||||||
|
@ -1158,9 +1160,9 @@ mod tests {
|
||||||
impl TestConn {
|
impl TestConn {
|
||||||
fn assert_materialized_views(&self) {
|
fn assert_materialized_views(&self) {
|
||||||
let materialized_ident_map = read_ident_map(&self.sqlite).expect("ident map");
|
let materialized_ident_map = read_ident_map(&self.sqlite).expect("ident map");
|
||||||
let materialized_schema_map = read_schema_map(&self.sqlite).expect("schema map");
|
let materialized_attribute_map = read_attribute_map(&self.sqlite).expect("schema map");
|
||||||
|
|
||||||
let materialized_schema = Schema::from_ident_map_and_schema_map(materialized_ident_map, materialized_schema_map).expect("schema");
|
let materialized_schema = Schema::from_ident_map_and_attribute_map(materialized_ident_map, materialized_attribute_map).expect("schema");
|
||||||
assert_eq!(materialized_schema, self.schema);
|
assert_eq!(materialized_schema, self.schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ use edn;
|
||||||
use entids;
|
use entids;
|
||||||
use errors::Result;
|
use errors::Result;
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
|
HasSchema,
|
||||||
SQLValueType,
|
SQLValueType,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
ValueType,
|
ValueType,
|
||||||
|
|
|
@ -55,6 +55,8 @@ pub use bootstrap::{
|
||||||
USER0,
|
USER0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub use schema::AttributeBuilder;
|
||||||
|
|
||||||
pub use db::{
|
pub use db::{
|
||||||
TypedSQLValue,
|
TypedSQLValue,
|
||||||
new_connection,
|
new_connection,
|
||||||
|
|
|
@ -43,7 +43,7 @@ use mentat_core::{
|
||||||
attribute,
|
attribute,
|
||||||
Entid,
|
Entid,
|
||||||
Schema,
|
Schema,
|
||||||
SchemaMap,
|
AttributeMap,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
ValueType,
|
ValueType,
|
||||||
};
|
};
|
||||||
|
@ -78,24 +78,24 @@ pub enum IdentAlteration {
|
||||||
/// Summarizes changes to metadata such as a a `Schema` and (in the future) a `PartitionMap`.
|
/// Summarizes changes to metadata such as a a `Schema` and (in the future) a `PartitionMap`.
|
||||||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
|
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
|
||||||
pub struct MetadataReport {
|
pub struct MetadataReport {
|
||||||
// Entids that were not present in the original `SchemaMap` that was mutated.
|
// Entids that were not present in the original `AttributeMap` that was mutated.
|
||||||
pub attributes_installed: BTreeSet<Entid>,
|
pub attributes_installed: BTreeSet<Entid>,
|
||||||
|
|
||||||
// Entids that were present in the original `SchemaMap` that was mutated, together with a
|
// Entids that were present in the original `AttributeMap` that was mutated, together with a
|
||||||
// representation of the mutations that were applied.
|
// representation of the mutations that were applied.
|
||||||
pub attributes_altered: BTreeMap<Entid, Vec<AttributeAlteration>>,
|
pub attributes_altered: BTreeMap<Entid, Vec<AttributeAlteration>>,
|
||||||
|
|
||||||
// Idents that were installed into the `SchemaMap`.
|
// Idents that were installed into the `AttributeMap`.
|
||||||
pub idents_altered: BTreeMap<Entid, IdentAlteration>,
|
pub idents_altered: BTreeMap<Entid, IdentAlteration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update a `SchemaMap` in place from the given `[e a typed_value]` triples.
|
/// Update a `AttributeMap` in place from the given `[e a typed_value]` triples.
|
||||||
///
|
///
|
||||||
/// This is suitable for producing a `SchemaMap` from the `schema` materialized view, which does not
|
/// This is suitable for producing a `AttributeMap` from the `schema` materialized view, which does not
|
||||||
/// contain install and alter markers.
|
/// contain install and alter markers.
|
||||||
///
|
///
|
||||||
/// Returns a report summarizing the mutations that were applied.
|
/// Returns a report summarizing the mutations that were applied.
|
||||||
pub fn update_schema_map_from_entid_triples<U>(schema_map: &mut SchemaMap, assertions: U) -> Result<MetadataReport>
|
pub fn update_attribute_map_from_entid_triples<U>(attribute_map: &mut AttributeMap, assertions: U) -> Result<MetadataReport>
|
||||||
where U: IntoIterator<Item=(Entid, Entid, TypedValue)> {
|
where U: IntoIterator<Item=(Entid, Entid, TypedValue)> {
|
||||||
|
|
||||||
// Group mutations by impacted entid.
|
// Group mutations by impacted entid.
|
||||||
|
@ -142,8 +142,8 @@ pub fn update_schema_map_from_entid_triples<U>(schema_map: &mut SchemaMap, asser
|
||||||
// builder.unique_value(false);
|
// builder.unique_value(false);
|
||||||
// builder.unique_identity(false);
|
// builder.unique_identity(false);
|
||||||
// },
|
// },
|
||||||
TypedValue::Ref(entids::DB_UNIQUE_VALUE) => { builder.unique(Some(attribute::Unique::Value)); },
|
TypedValue::Ref(entids::DB_UNIQUE_VALUE) => { builder.unique(attribute::Unique::Value); },
|
||||||
TypedValue::Ref(entids::DB_UNIQUE_IDENTITY) => { builder.unique(Some(attribute::Unique::Identity)); },
|
TypedValue::Ref(entids::DB_UNIQUE_IDENTITY) => { builder.unique(attribute::Unique::Identity); },
|
||||||
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/unique :db.unique/value|:db.unique/identity] but got [... :db/unique {:?}]", value)))
|
_ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/unique :db.unique/value|:db.unique/identity] but got [... :db/unique {:?}]", value)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -179,7 +179,7 @@ pub fn update_schema_map_from_entid_triples<U>(schema_map: &mut SchemaMap, asser
|
||||||
let mut attributes_altered: BTreeMap<Entid, Vec<AttributeAlteration>> = BTreeMap::default();
|
let mut attributes_altered: BTreeMap<Entid, Vec<AttributeAlteration>> = BTreeMap::default();
|
||||||
|
|
||||||
for (entid, builder) in builders.into_iter() {
|
for (entid, builder) in builders.into_iter() {
|
||||||
match schema_map.entry(entid) {
|
match attribute_map.entry(entid) {
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
builder.validate_install_attribute()
|
builder.validate_install_attribute()
|
||||||
.chain_err(|| ErrorKind::BadSchemaAssertion(format!("Schema alteration for new attribute with entid {} is not valid", entid)))?;
|
.chain_err(|| ErrorKind::BadSchemaAssertion(format!("Schema alteration for new attribute with entid {} is not valid", entid)))?;
|
||||||
|
@ -245,7 +245,7 @@ pub fn update_schema_from_entid_quadruples<U>(schema: &mut Schema, assertions: U
|
||||||
let asserted_triples = attribute_set.asserted.into_iter().map(|((e, a), typed_value)| (e, a, typed_value));
|
let asserted_triples = attribute_set.asserted.into_iter().map(|((e, a), typed_value)| (e, a, typed_value));
|
||||||
let altered_triples = attribute_set.altered.into_iter().map(|((e, a), (_old_value, new_value))| (e, a, new_value));
|
let altered_triples = attribute_set.altered.into_iter().map(|((e, a), (_old_value, new_value))| (e, a, new_value));
|
||||||
|
|
||||||
let report = update_schema_map_from_entid_triples(&mut schema.schema_map, asserted_triples.chain(altered_triples))?;
|
let report = update_attribute_map_from_entid_triples(&mut schema.attribute_map, asserted_triples.chain(altered_triples))?;
|
||||||
|
|
||||||
let mut idents_altered: BTreeMap<Entid, IdentAlteration> = BTreeMap::new();
|
let mut idents_altered: BTreeMap<Entid, IdentAlteration> = BTreeMap::new();
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,10 @@ use mentat_core::{
|
||||||
Attribute,
|
Attribute,
|
||||||
Entid,
|
Entid,
|
||||||
EntidMap,
|
EntidMap,
|
||||||
|
HasSchema,
|
||||||
IdentMap,
|
IdentMap,
|
||||||
Schema,
|
Schema,
|
||||||
SchemaMap,
|
AttributeMap,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
ValueType,
|
ValueType,
|
||||||
};
|
};
|
||||||
|
@ -30,9 +31,9 @@ use metadata::{
|
||||||
AttributeAlteration,
|
AttributeAlteration,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Return `Ok(())` if `schema_map` defines a valid Mentat schema.
|
/// Return `Ok(())` if `attribute_map` defines a valid Mentat schema.
|
||||||
fn validate_schema_map(entid_map: &EntidMap, schema_map: &SchemaMap) -> Result<()> {
|
fn validate_attribute_map(entid_map: &EntidMap, attribute_map: &AttributeMap) -> Result<()> {
|
||||||
for (entid, attribute) in schema_map {
|
for (entid, attribute) in attribute_map {
|
||||||
let ident = || entid_map.get(entid).map(|ident| ident.to_string()).unwrap_or(entid.to_string());
|
let ident = || entid_map.get(entid).map(|ident| ident.to_string()).unwrap_or(entid.to_string());
|
||||||
if attribute.unique == Some(attribute::Unique::Value) && !attribute.index {
|
if attribute.unique == Some(attribute::Unique::Value) && !attribute.index {
|
||||||
bail!(ErrorKind::BadSchemaAssertion(format!(":db/unique :db/unique_value without :db/index true for entid: {}", ident())))
|
bail!(ErrorKind::BadSchemaAssertion(format!(":db/unique :db/unique_value without :db/index true for entid: {}", ident())))
|
||||||
|
@ -78,8 +79,8 @@ impl AttributeBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unique<'a>(&'a mut self, unique: Option<attribute::Unique>) -> &'a mut Self {
|
pub fn unique<'a>(&'a mut self, unique: attribute::Unique) -> &'a mut Self {
|
||||||
self.unique = Some(unique);
|
self.unique = Some(Some(unique));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +175,7 @@ pub trait SchemaBuilding {
|
||||||
fn require_ident(&self, entid: Entid) -> Result<&symbols::NamespacedKeyword>;
|
fn require_ident(&self, entid: Entid) -> Result<&symbols::NamespacedKeyword>;
|
||||||
fn require_entid(&self, ident: &symbols::NamespacedKeyword) -> 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_attribute_map(ident_map: IdentMap, attribute_map: AttributeMap) -> 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=(symbols::NamespacedKeyword, symbols::NamespacedKeyword, TypedValue)>;
|
where U: IntoIterator<Item=(symbols::NamespacedKeyword, symbols::NamespacedKeyword, TypedValue)>;
|
||||||
}
|
}
|
||||||
|
@ -193,15 +194,15 @@ impl SchemaBuilding for Schema {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a valid `Schema` from the constituent maps.
|
/// Create a valid `Schema` from the constituent maps.
|
||||||
fn from_ident_map_and_schema_map(ident_map: IdentMap, schema_map: SchemaMap) -> Result<Schema> {
|
fn from_ident_map_and_attribute_map(ident_map: IdentMap, attribute_map: AttributeMap) -> Result<Schema> {
|
||||||
let entid_map: EntidMap = ident_map.iter().map(|(k, v)| (v.clone(), k.clone())).collect();
|
let entid_map: EntidMap = ident_map.iter().map(|(k, v)| (v.clone(), k.clone())).collect();
|
||||||
|
|
||||||
validate_schema_map(&entid_map, &schema_map)?;
|
validate_attribute_map(&entid_map, &attribute_map)?;
|
||||||
|
|
||||||
Ok(Schema {
|
Ok(Schema {
|
||||||
ident_map: ident_map,
|
ident_map: ident_map,
|
||||||
entid_map: entid_map,
|
entid_map: entid_map,
|
||||||
schema_map: schema_map,
|
attribute_map: attribute_map,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,8 +215,8 @@ impl SchemaBuilding for Schema {
|
||||||
let attr: i64 = *ident_map.get(&symbolic_attr).ok_or(ErrorKind::UnrecognizedIdent(symbolic_attr.to_string()))?;
|
let attr: i64 = *ident_map.get(&symbolic_attr).ok_or(ErrorKind::UnrecognizedIdent(symbolic_attr.to_string()))?;
|
||||||
Ok((ident, attr, value))
|
Ok((ident, attr, value))
|
||||||
}).collect();
|
}).collect();
|
||||||
let mut schema = Schema::from_ident_map_and_schema_map(ident_map, SchemaMap::default())?;
|
let mut schema = Schema::from_ident_map_and_attribute_map(ident_map, AttributeMap::default())?;
|
||||||
metadata::update_schema_map_from_entid_triples(&mut schema.schema_map, entid_assertions?)?;
|
metadata::update_attribute_map_from_entid_triples(&mut schema.attribute_map, entid_assertions?)?;
|
||||||
Ok(schema)
|
Ok(schema)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -283,11 +284,11 @@ mod test {
|
||||||
schema.entid_map.insert(entid, ident.clone());
|
schema.entid_map.insert(entid, ident.clone());
|
||||||
schema.ident_map.insert(ident.clone(), entid);
|
schema.ident_map.insert(ident.clone(), entid);
|
||||||
|
|
||||||
schema.schema_map.insert(entid, attribute);
|
schema.attribute_map.insert(entid, attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn validate_schema_map_success() {
|
fn validate_attribute_map_success() {
|
||||||
let mut schema = Schema::default();
|
let mut schema = Schema::default();
|
||||||
// attribute that is not an index has no uniqueness
|
// attribute that is not an index has no uniqueness
|
||||||
add_attribute(&mut schema, NamespacedKeyword::new("foo", "bar"), 97, Attribute {
|
add_attribute(&mut schema, NamespacedKeyword::new("foo", "bar"), 97, Attribute {
|
||||||
|
@ -335,7 +336,7 @@ mod test {
|
||||||
component: false,
|
component: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
assert!(validate_schema_map(&schema.entid_map, &schema.schema_map).is_ok());
|
assert!(validate_attribute_map(&schema.entid_map, &schema.attribute_map).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -352,7 +353,7 @@ mod test {
|
||||||
component: false,
|
component: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = validate_schema_map(&schema.entid_map, &schema.schema_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
||||||
assert!(err.is_some());
|
assert!(err.is_some());
|
||||||
|
|
||||||
match err.unwrap() {
|
match err.unwrap() {
|
||||||
|
@ -374,7 +375,7 @@ mod test {
|
||||||
component: false,
|
component: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = validate_schema_map(&schema.entid_map, &schema.schema_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
||||||
assert!(err.is_some());
|
assert!(err.is_some());
|
||||||
|
|
||||||
match err.unwrap() {
|
match err.unwrap() {
|
||||||
|
@ -396,7 +397,7 @@ mod test {
|
||||||
component: true,
|
component: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = validate_schema_map(&schema.entid_map, &schema.schema_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
||||||
assert!(err.is_some());
|
assert!(err.is_some());
|
||||||
|
|
||||||
match err.unwrap() {
|
match err.unwrap() {
|
||||||
|
@ -418,7 +419,7 @@ mod test {
|
||||||
component: false,
|
component: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = validate_schema_map(&schema.entid_map, &schema.schema_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
||||||
assert!(err.is_some());
|
assert!(err.is_some());
|
||||||
|
|
||||||
match err.unwrap() {
|
match err.unwrap() {
|
||||||
|
@ -439,7 +440,7 @@ mod test {
|
||||||
component: false,
|
component: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let err = validate_schema_map(&schema.entid_map, &schema.schema_map).err();
|
let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err();
|
||||||
assert!(err.is_some());
|
assert!(err.is_some());
|
||||||
|
|
||||||
match err.unwrap() {
|
match err.unwrap() {
|
||||||
|
|
18
db/src/tx.rs
18
db/src/tx.rs
|
@ -85,8 +85,10 @@ use mentat_core::{
|
||||||
Schema,
|
Schema,
|
||||||
Utc,
|
Utc,
|
||||||
attribute,
|
attribute,
|
||||||
intern_set,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use mentat_core::intern_set::InternSet;
|
||||||
|
|
||||||
use mentat_tx::entities as entmod;
|
use mentat_tx::entities as entmod;
|
||||||
use mentat_tx::entities::{
|
use mentat_tx::entities::{
|
||||||
Entity,
|
Entity,
|
||||||
|
@ -199,13 +201,13 @@ impl<'conn, 'a> Tx<'conn, 'a> {
|
||||||
///
|
///
|
||||||
/// The `Term` instances produce share interned TempId and LookupRef handles, and we return the
|
/// The `Term` instances produce share interned TempId and LookupRef handles, and we return the
|
||||||
/// interned handle sets so that consumers can ensure all handles are used appropriately.
|
/// interned handle sets so that consumers can ensure all handles are used appropriately.
|
||||||
fn entities_into_terms_with_temp_ids_and_lookup_refs<I>(&self, entities: I) -> Result<(Vec<TermWithTempIdsAndLookupRefs>, intern_set::InternSet<TempId>, intern_set::InternSet<AVPair>)> where I: IntoIterator<Item=Entity> {
|
fn entities_into_terms_with_temp_ids_and_lookup_refs<I>(&self, entities: I) -> Result<(Vec<TermWithTempIdsAndLookupRefs>, InternSet<TempId>, InternSet<AVPair>)> where I: IntoIterator<Item=Entity> {
|
||||||
struct InProcess<'a> {
|
struct InProcess<'a> {
|
||||||
partition_map: &'a PartitionMap,
|
partition_map: &'a PartitionMap,
|
||||||
schema: &'a Schema,
|
schema: &'a Schema,
|
||||||
mentat_id_count: i64,
|
mentat_id_count: i64,
|
||||||
temp_ids: intern_set::InternSet<TempId>,
|
temp_ids: InternSet<TempId>,
|
||||||
lookup_refs: intern_set::InternSet<AVPair>,
|
lookup_refs: InternSet<AVPair>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> InProcess<'a> {
|
impl<'a> InProcess<'a> {
|
||||||
|
@ -214,8 +216,8 @@ impl<'conn, 'a> Tx<'conn, 'a> {
|
||||||
partition_map,
|
partition_map,
|
||||||
schema,
|
schema,
|
||||||
mentat_id_count: 0,
|
mentat_id_count: 0,
|
||||||
temp_ids: intern_set::InternSet::new(),
|
temp_ids: InternSet::new(),
|
||||||
lookup_refs: intern_set::InternSet::new(),
|
lookup_refs: InternSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -685,6 +687,8 @@ impl<'conn, 'a> Tx<'conn, 'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transact the given `entities` against the given SQLite `conn`, using the given metadata.
|
/// Transact the given `entities` against the given SQLite `conn`, using the given metadata.
|
||||||
|
/// If you want this work to occur inside a SQLite transaction, establish one on the connection
|
||||||
|
/// prior to calling this function.
|
||||||
///
|
///
|
||||||
/// This approach is explained in https://github.com/mozilla/mentat/wiki/Transacting.
|
/// This approach is explained in https://github.com/mozilla/mentat/wiki/Transacting.
|
||||||
// TODO: move this to the transactor layer.
|
// TODO: move this to the transactor layer.
|
||||||
|
@ -694,8 +698,6 @@ pub fn transact<'conn, 'a, I>(
|
||||||
schema_for_mutation: &'a Schema,
|
schema_for_mutation: &'a Schema,
|
||||||
schema: &'a Schema,
|
schema: &'a Schema,
|
||||||
entities: I) -> Result<(TxReport, PartitionMap, Option<Schema>)> where I: IntoIterator<Item=Entity> {
|
entities: I) -> Result<(TxReport, PartitionMap, Option<Schema>)> where I: IntoIterator<Item=Entity> {
|
||||||
// Eventually, this function will be responsible for managing a SQLite transaction. For
|
|
||||||
// now, it's just about the tx details.
|
|
||||||
|
|
||||||
let tx_instant = ::now(); // Label the transaction with the timestamp when we first see it: leading edge.
|
let tx_instant = ::now(); // Label the transaction with the timestamp when we first see it: leading edge.
|
||||||
let tx_id = partition_map.allocate_entid(":db.part/tx");
|
let tx_id = partition_map.allocate_entid(":db.part/tx");
|
||||||
|
|
|
@ -83,7 +83,6 @@ pub type AVPair = (Entid, TypedValue);
|
||||||
pub type AVMap<'a> = HashMap<&'a AVPair, Entid>;
|
pub type AVMap<'a> = HashMap<&'a AVPair, Entid>;
|
||||||
|
|
||||||
/// A transaction report summarizes an applied transaction.
|
/// A transaction report summarizes an applied transaction.
|
||||||
// TODO: include map of resolved tempids.
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
|
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
|
||||||
pub struct TxReport {
|
pub struct TxReport {
|
||||||
/// The transaction ID of the transaction.
|
/// The transaction ID of the transaction.
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
|
HasSchema,
|
||||||
Schema,
|
Schema,
|
||||||
SQLValueType,
|
SQLValueType,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
|
HasSchema,
|
||||||
Schema,
|
Schema,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
ValueType,
|
ValueType,
|
||||||
|
|
|
@ -25,6 +25,7 @@ use std::fmt::{
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
Attribute,
|
Attribute,
|
||||||
Entid,
|
Entid,
|
||||||
|
HasSchema,
|
||||||
Schema,
|
Schema,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
ValueType,
|
ValueType,
|
||||||
|
@ -917,7 +918,7 @@ fn associate_ident(schema: &mut Schema, i: NamespacedKeyword, e: Entid) {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
|
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
|
||||||
schema.schema_map.insert(e, a);
|
schema.attribute_map.insert(e, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -79,6 +79,7 @@ mod testing {
|
||||||
|
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
Attribute,
|
Attribute,
|
||||||
|
Schema,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
ValueType,
|
ValueType,
|
||||||
ValueTypeSet,
|
ValueTypeSet,
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
|
HasSchema,
|
||||||
Schema,
|
Schema,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
ValueType,
|
ValueType,
|
||||||
|
|
|
@ -41,7 +41,7 @@ fn associate_ident(schema: &mut Schema, i: NamespacedKeyword, e: Entid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
|
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
|
||||||
schema.schema_map.insert(e, a);
|
schema.attribute_map.insert(e, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepopulated_schema() -> Schema {
|
fn prepopulated_schema() -> Schema {
|
||||||
|
|
|
@ -53,7 +53,7 @@ fn associate_ident(schema: &mut Schema, i: NamespacedKeyword, e: Entid) {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
|
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
|
||||||
schema.schema_map.insert(e, a);
|
schema.attribute_map.insert(e, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepopulated_schema() -> Schema {
|
fn prepopulated_schema() -> Schema {
|
||||||
|
|
|
@ -48,7 +48,7 @@ fn associate_ident(schema: &mut Schema, i: NamespacedKeyword, e: Entid) {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
|
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
|
||||||
schema.schema_map.insert(e, a);
|
schema.attribute_map.insert(e, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepopulated_schema() -> Schema {
|
fn prepopulated_schema() -> Schema {
|
||||||
|
|
|
@ -50,7 +50,7 @@ fn associate_ident(schema: &mut Schema, i: NamespacedKeyword, e: Entid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
|
fn add_attribute(schema: &mut Schema, e: Entid, a: Attribute) {
|
||||||
schema.schema_map.insert(e, a);
|
schema.attribute_map.insert(e, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate_with_inputs(schema: &Schema, query: &'static str, inputs: QueryInputs) -> SQLQuery {
|
fn translate_with_inputs(schema: &Schema, query: &'static str, inputs: QueryInputs) -> SQLQuery {
|
||||||
|
|
|
@ -54,6 +54,7 @@ pub use mentat_db::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use query::{
|
pub use query::{
|
||||||
|
IntoResult,
|
||||||
NamespacedKeyword,
|
NamespacedKeyword,
|
||||||
PlainSymbol,
|
PlainSymbol,
|
||||||
QueryExplanation,
|
QueryExplanation,
|
||||||
|
|
|
@ -15,6 +15,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
Entid,
|
Entid,
|
||||||
|
HasSchema,
|
||||||
Schema,
|
Schema,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,6 +22,7 @@ use chrono::FixedOffset;
|
||||||
|
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
DateTime,
|
DateTime,
|
||||||
|
HasSchema,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
ValueType,
|
ValueType,
|
||||||
Utc,
|
Utc,
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
///! This module defines core types that support the transaction processor.
|
//! This module defines core types that support the transaction processor.
|
||||||
|
|
||||||
extern crate edn;
|
extern crate edn;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue