From 4d8e179a59c224d5eccd0cddc44fb6dea33f7fe8 Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Tue, 3 Apr 2018 14:25:53 -0700 Subject: [PATCH] Expose component_attributes on Schema. (#623) r=nalexander Some parts of the query engine and transactor need to know whether an attribute is a component attribute, and sometimes want to do so in a generated SQL query. This is one way to do that. --- core/src/lib.rs | 26 ++++++++++++++++++++++++++ db/src/metadata.rs | 11 +++++++++++ db/src/schema.rs | 19 ++++++++++++------- src/conn.rs | 8 ++++++++ 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index 7f809c4d..b70ec7e8 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -809,6 +809,10 @@ pub struct Schema { /// Invariant: key-set is the same as the key-set of `entid_map` (equivalently, the value-set of /// `ident_map`). pub attribute_map: AttributeMap, + + /// Maintain a vec of unique attribute IDs for which the corresponding attribute in `attribute_map` + /// has `.component == true`. + pub component_attributes: Vec, } pub trait HasSchema { @@ -826,9 +830,17 @@ pub trait HasSchema { /// Return true if the provided ident identifies an attribute in this schema. fn identifies_attribute(&self, x: &NamespacedKeyword) -> bool; + + fn component_attributes(&self) -> &[Entid]; } impl Schema { + pub fn new(ident_map: IdentMap, entid_map: EntidMap, attribute_map: AttributeMap) -> Schema { + let mut s = Schema { ident_map, entid_map, attribute_map, component_attributes: Vec::new() }; + s.update_component_attributes(); + s + } + /// 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() @@ -840,6 +852,16 @@ impl Schema { fn get_raw_entid(&self, x: &NamespacedKeyword) -> Option { self.ident_map.get(x).map(|x| *x) } + + pub fn update_component_attributes(&mut self) { + let mut components: Vec; + components = self.attribute_map + .iter() + .filter_map(|(k, v)| if v.component { Some(*k) } else { None }) + .collect(); + components.sort_unstable(); + self.component_attributes = components; + } } impl HasSchema for Schema { @@ -876,6 +898,10 @@ impl HasSchema for Schema { fn identifies_attribute(&self, x: &NamespacedKeyword) -> bool { self.get_raw_entid(x).map(|e| self.is_attribute(e)).unwrap_or(false) } + + fn component_attributes(&self) -> &[Entid] { + &self.component_attributes + } } #[cfg(test)] diff --git a/db/src/metadata.rs b/db/src/metadata.rs index 30b18670..9e970979 100644 --- a/db/src/metadata.rs +++ b/db/src/metadata.rs @@ -91,6 +91,13 @@ pub struct MetadataReport { pub idents_altered: BTreeMap, } +impl MetadataReport { + pub fn attributes_did_change(&self) -> bool { + !(self.attributes_installed.is_empty() && + self.attributes_altered.is_empty()) + } +} + /// Update a `AttributeMap` in place from the given `[e a typed_value]` triples. /// /// This is suitable for producing a `AttributeMap` from the `schema` materialized view, which does not @@ -284,6 +291,10 @@ pub fn update_schema_from_entid_quadruples(schema: &mut Schema, assertions: U idents_altered.insert(entid, IdentAlteration::Ident(ident.clone())); } + if report.attributes_did_change() { + schema.update_component_attributes(); + } + Ok(MetadataReport { idents_altered: idents_altered, .. report diff --git a/db/src/schema.rs b/db/src/schema.rs index a39069a6..6c0d1d27 100644 --- a/db/src/schema.rs +++ b/db/src/schema.rs @@ -241,12 +241,7 @@ impl SchemaBuilding for Schema { let entid_map: EntidMap = ident_map.iter().map(|(k, v)| (v.clone(), k.clone())).collect(); validate_attribute_map(&entid_map, &attribute_map)?; - - Ok(Schema { - ident_map: ident_map, - entid_map: entid_map, - attribute_map: attribute_map, - }) + Ok(Schema::new(ident_map, entid_map, attribute_map)) } /// Turn vec![(NamespacedKeyword(:ident), NamespacedKeyword(:key), TypedValue(:value)), ...] into a Mentat `Schema`. @@ -258,8 +253,14 @@ impl SchemaBuilding for Schema { let attr: i64 = *ident_map.get(&symbolic_attr).ok_or(ErrorKind::UnrecognizedIdent(symbolic_attr.to_string()))?; Ok((ident, attr, value)) }).collect(); + let mut schema = Schema::from_ident_map_and_attribute_map(ident_map, AttributeMap::default())?; - metadata::update_attribute_map_from_entid_triples(&mut schema.attribute_map, entid_assertions?)?; + let metadata_report = metadata::update_attribute_map_from_entid_triples(&mut schema.attribute_map, entid_assertions?)?; + + // Rebuild the component attributes list if necessary. + if metadata_report.attributes_did_change() { + schema.update_component_attributes(); + } Ok(schema) } } @@ -327,6 +328,10 @@ mod test { schema.entid_map.insert(entid, ident.clone()); schema.ident_map.insert(ident.clone(), entid); + if attribute.component { + schema.component_attributes.push(entid); + } + schema.attribute_map.insert(entid, attribute); } diff --git a/src/conn.rs b/src/conn.rs index 24838121..822c3e8c 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -336,6 +336,10 @@ impl<'a, 'c> HasSchema for InProgressRead<'a, 'c> { fn identifies_attribute(&self, x: &NamespacedKeyword) -> bool { self.0.identifies_attribute(x) } + + fn component_attributes(&self) -> &[Entid] { + self.0.component_attributes() + } } impl<'a, 'c> HasSchema for InProgress<'a, 'c> { @@ -368,6 +372,10 @@ impl<'a, 'c> HasSchema for InProgress<'a, 'c> { fn identifies_attribute(&self, x: &NamespacedKeyword) -> bool { self.schema.identifies_attribute(x) } + + fn component_attributes(&self) -> &[Entid] { + self.schema.component_attributes() + } }