diff --git a/core/src/lib.rs b/core/src/lib.rs index 47ac4e15..e6c60d12 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -700,6 +700,9 @@ pub struct Attribute { /// They are used to compose entities from component sub-entities: they are fetched recursively /// by pull expressions, and they are automatically recursively deleted where appropriate. pub component: bool, + + /// `true` if this attribute doesn't require history to be kept, i.e., it is `:db/noHistory true`. + pub no_history: bool, } impl Attribute { @@ -750,6 +753,10 @@ impl Attribute { attribute_map.insert(values::DB_IS_COMPONENT.clone(), edn::Value::Boolean(true)); } + if self.no_history { + attribute_map.insert(values::DB_NO_HISTORY.clone(), edn::Value::Boolean(true)); + } + edn::Value::Map(attribute_map) } } @@ -764,6 +771,7 @@ impl Default for Attribute { multival: false, unique: None, component: false, + no_history: false, } } } @@ -894,6 +902,7 @@ mod test { unique: None, multival: false, component: false, + no_history: false, }; assert!(attr1.flags() & AttributeBitFlags::IndexAVET as u8 != 0); @@ -908,6 +917,7 @@ mod test { unique: Some(attribute::Unique::Value), multival: false, component: false, + no_history: false, }; assert!(attr2.flags() & AttributeBitFlags::IndexAVET as u8 == 0); @@ -922,6 +932,7 @@ mod test { unique: Some(attribute::Unique::Identity), multival: false, component: false, + no_history: false, }; assert!(attr3.flags() & AttributeBitFlags::IndexAVET as u8 == 0); @@ -954,6 +965,7 @@ mod test { unique: None, multival: false, component: false, + no_history: true, }; associate_ident(&mut schema, NamespacedKeyword::new("foo", "bar"), 97); add_attribute(&mut schema, 97, attr1); @@ -965,6 +977,7 @@ mod test { unique: Some(attribute::Unique::Value), multival: true, component: false, + no_history: false, }; associate_ident(&mut schema, NamespacedKeyword::new("foo", "bas"), 98); add_attribute(&mut schema, 98, attr2); @@ -976,6 +989,7 @@ mod test { unique: Some(attribute::Unique::Identity), multival: false, component: true, + no_history: false, }; associate_ident(&mut schema, NamespacedKeyword::new("foo", "bat"), 99); @@ -986,7 +1000,8 @@ mod test { let expected_output = r#"[ { :db/ident :foo/bar :db/valueType :db.type/ref :db/cardinality :db.cardinality/one - :db/index true }, + :db/index true + :db/noHistory true }, { :db/ident :foo/bas :db/valueType :db.type/string :db/cardinality :db.cardinality/many diff --git a/core/src/values.rs b/core/src/values.rs index 3a5fceba..8502a8cd 100644 --- a/core/src/values.rs +++ b/core/src/values.rs @@ -47,6 +47,7 @@ lazy_static_namespaced_keyword_value!(DB_IDENT, "db", "ident"); lazy_static_namespaced_keyword_value!(DB_INDEX, "db", "index"); lazy_static_namespaced_keyword_value!(DB_INSTALL_ATTRIBUTE, "db.install", "attribute"); lazy_static_namespaced_keyword_value!(DB_IS_COMPONENT, "db", "component"); +lazy_static_namespaced_keyword_value!(DB_NO_HISTORY, "db", "noHistory"); lazy_static_namespaced_keyword_value!(DB_PART_DB, "db.part", "db"); lazy_static_namespaced_keyword_value!(DB_RETRACT, "db", "retract"); lazy_static_namespaced_keyword_value!(DB_TYPE_BOOLEAN, "db.type", "boolean"); diff --git a/db/src/metadata.rs b/db/src/metadata.rs index 00e0c4ac..30b18670 100644 --- a/db/src/metadata.rs +++ b/db/src/metadata.rs @@ -171,6 +171,13 @@ pub fn update_attribute_map_from_entid_triples(attribute_map: &mut AttributeM } }, + entids::DB_NO_HISTORY => { + match *value { + TypedValue::Boolean(x) => { builder.no_history(x); }, + _ => bail!(ErrorKind::BadSchemaAssertion(format!("Expected [... :db/noHistory true|false] but got [... :db/noHistory {:?}]", value))) + } + }, + _ => { bail!(ErrorKind::BadSchemaAssertion(format!("Do not recognize attribute {} for entid {}", attr, entid))) } diff --git a/db/src/schema.rs b/db/src/schema.rs index 96707c3b..a39069a6 100644 --- a/db/src/schema.rs +++ b/db/src/schema.rs @@ -79,6 +79,7 @@ pub struct AttributeBuilder { index: Option, fulltext: Option, component: Option, + no_history: Option, } impl AttributeBuilder { @@ -127,6 +128,11 @@ impl AttributeBuilder { self } + pub fn no_history<'a>(&'a mut self, no_history: bool) -> &'a mut Self { + self.no_history = Some(no_history); + self + } + pub fn validate_install_attribute(&self) -> Result<()> { if self.value_type.is_none() { bail!(ErrorKind::BadSchemaAssertion("Schema attribute for new attribute does not set :db/valueType".into())); @@ -164,6 +170,9 @@ impl AttributeBuilder { if let Some(component) = self.component { attribute.component = component; } + if let Some(no_history) = self.no_history { + attribute.no_history = no_history; + } attribute } @@ -194,6 +203,12 @@ impl AttributeBuilder { mutations.push(AttributeAlteration::IsComponent); } } + if let Some(no_history) = self.no_history { + if no_history != attribute.no_history { + attribute.no_history = no_history; + mutations.push(AttributeAlteration::NoHistory); + } + } mutations } @@ -326,6 +341,7 @@ mod test { unique: None, multival: false, component: false, + no_history: false, }); // attribute is unique by value and an index add_attribute(&mut schema, NamespacedKeyword::new("foo", "baz"), 98, Attribute { @@ -335,6 +351,7 @@ mod test { unique: Some(attribute::Unique::Value), multival: false, component: false, + no_history: false, }); // attribue is unique by identity and an index add_attribute(&mut schema, NamespacedKeyword::new("foo", "bat"), 99, Attribute { @@ -344,6 +361,7 @@ mod test { unique: Some(attribute::Unique::Identity), multival: false, component: false, + no_history: false, }); // attribute is a components and a `Ref` add_attribute(&mut schema, NamespacedKeyword::new("foo", "bak"), 100, Attribute { @@ -353,6 +371,7 @@ mod test { unique: None, multival: false, component: true, + no_history: false, }); // fulltext attribute is a string and an index add_attribute(&mut schema, NamespacedKeyword::new("foo", "bap"), 101, Attribute { @@ -362,6 +381,7 @@ mod test { unique: None, multival: false, component: false, + no_history: false, }); assert!(validate_attribute_map(&schema.entid_map, &schema.attribute_map).is_ok()); @@ -379,6 +399,7 @@ mod test { unique: Some(attribute::Unique::Value), multival: false, component: false, + no_history: false, }); let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err(); @@ -401,6 +422,7 @@ mod test { unique: Some(attribute::Unique::Identity), multival: false, component: false, + no_history: false, }); let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err(); @@ -423,6 +445,7 @@ mod test { unique: None, multival: false, component: true, + no_history: false, }); let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err(); @@ -445,6 +468,7 @@ mod test { unique: None, multival: false, component: false, + no_history: false, }); let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err(); @@ -466,6 +490,7 @@ mod test { unique: None, multival: false, component: false, + no_history: false, }); let err = validate_attribute_map(&schema.entid_map, &schema.attribute_map).err(); diff --git a/src/vocabulary.rs b/src/vocabulary.rs index 4b73a53d..30993fc8 100644 --- a/src/vocabulary.rs +++ b/src/vocabulary.rs @@ -207,10 +207,9 @@ lazy_static! { kw!(:db.cardinality/many) }; - // Not yet supported. - // static ref DB_NO_HISTORY: NamespacedKeyword = { - // NamespacedKeyword::new("db", "noHistory") - // }; + static ref DB_NO_HISTORY: NamespacedKeyword = { + NamespacedKeyword::new("db", "noHistory") + }; } trait HasCoreSchema { @@ -260,8 +259,7 @@ impl Definition { let a_value_type = via.core_attribute(&DB_VALUE_TYPE)?; let a_unique = via.core_attribute(&DB_UNIQUE)?; - // Not yet supported. - // let a_no_history = via.core_attribute(&DB_NO_HISTORY)?; + let a_no_history = via.core_attribute(&DB_NO_HISTORY)?; let v_cardinality_many = via.core_entid(&DB_CARDINALITY_MANY)?; let v_cardinality_one = via.core_entid(&DB_CARDINALITY_ONE)?; @@ -310,6 +308,9 @@ impl Definition { if attr.component { builder.add(tempid.clone(), a_is_component, TypedValue::Boolean(true))?; } + if attr.no_history { + builder.add(tempid.clone(), a_no_history, TypedValue::Boolean(true))?; + } if let Some(u) = attr.unique { let uu = match u {