// Copyright 2016 Mozilla // // Licensed under the Apache License, Version 2.0 (the "License"); you may not use // this file except in compliance with the License. You may obtain a copy of the // License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. use std::collections::{ BTreeMap, BTreeSet, }; use std::iter::Peekable; use rusqlite; use mentat_core::{ CachedAttributes, Entid, HasSchema, Schema, TypedValue, }; use db::{ TypedSQLValue, }; use errors::{ ErrorKind, Result, }; pub type Aev = (Entid, Entid, TypedValue); fn row_to_aev(row: &rusqlite::Row) -> Aev { let a: Entid = row.get(0); let e: Entid = row.get(1); let value_type_tag: i32 = row.get(3); let v = TypedValue::from_sql_value_pair(row.get(2), value_type_tag).map(|x| x).unwrap(); (a, e, v) } pub type CacheMap = BTreeMap; pub struct AevRows<'conn> { rows: rusqlite::MappedRows<'conn, fn(&rusqlite::Row) -> Aev>, } /// Unwrap the Result from MappedRows. We could also use this opportunity to map_err it, but /// for now it's convenient to avoid error handling. impl<'conn> Iterator for AevRows<'conn> { type Item = Aev; fn next(&mut self) -> Option { self.rows .next() .map(|row_result| row_result.expect("All database contents should be representable")) } } // The behavior of the cache is different for different kinds of attributes: // - cardinality/one doesn't need a vec // - unique/* should have a bijective mapping (reverse lookup) trait CardinalityOneCache { fn clear(&mut self); fn set(&mut self, e: Entid, v: TypedValue); fn get(&self, e: Entid) -> Option<&TypedValue>; } trait CardinalityManyCache { fn clear(&mut self); fn acc(&mut self, e: Entid, v: TypedValue); fn set(&mut self, e: Entid, vs: Vec); fn get(&self, e: Entid) -> Option<&Vec>; } #[derive(Debug, Default)] struct SingleValAttributeCache { attr: Entid, e_v: CacheMap, } impl CardinalityOneCache for SingleValAttributeCache { fn clear(&mut self) { self.e_v.clear(); } fn set(&mut self, e: Entid, v: TypedValue) { self.e_v.insert(e, v); } fn get(&self, e: Entid) -> Option<&TypedValue> { self.e_v.get(&e) } } #[derive(Debug, Default)] struct MultiValAttributeCache { attr: Entid, e_vs: CacheMap>, } impl CardinalityManyCache for MultiValAttributeCache { fn clear(&mut self) { self.e_vs.clear(); } fn acc(&mut self, e: Entid, v: TypedValue) { self.e_vs.entry(e).or_insert(vec![]).push(v) } fn set(&mut self, e: Entid, vs: Vec) { self.e_vs.insert(e, vs); } fn get(&self, e: Entid) -> Option<&Vec> { self.e_vs.get(&e) } } #[derive(Debug, Default)] struct UniqueReverseAttributeCache { attr: Entid, v_e: CacheMap, } impl UniqueReverseAttributeCache { fn clear(&mut self) { self.v_e.clear(); } fn set(&mut self, e: Entid, v: TypedValue) { self.v_e.insert(v, e); } fn get_e(&self, v: &TypedValue) -> Option { self.v_e.get(v).cloned() } } #[derive(Debug, Default)] struct NonUniqueReverseAttributeCache { attr: Entid, v_es: CacheMap>, } impl NonUniqueReverseAttributeCache { fn clear(&mut self) { self.v_es.clear(); } fn acc(&mut self, e: Entid, v: TypedValue) { self.v_es.entry(v).or_insert(BTreeSet::new()).insert(e); } fn get_es(&self, v: &TypedValue) -> Option<&BTreeSet> { self.v_es.get(v) } } #[derive(Debug, Default)] pub struct AttributeCaches { reverse_cached_attributes: BTreeSet, forward_cached_attributes: BTreeSet, single_vals: BTreeMap, multi_vals: BTreeMap, unique_reverse: BTreeMap, non_unique_reverse: BTreeMap, } fn with_aev_iter(a: Entid, iter: &mut Peekable, mut f: F) where I: Iterator, F: FnMut(Entid, TypedValue) { let check = Some(a); while iter.peek().map(|&(a, _, _)| a) == check { let (_, e, v) = iter.next().unwrap(); f(e, v); } } fn accumulate_single_val_evs_forward(a: Entid, f: &mut C, iter: &mut Peekable) where I: Iterator, C: CardinalityOneCache { with_aev_iter(a, iter, |e, v| f.set(e, v)) } fn accumulate_multi_val_evs_forward(a: Entid, f: &mut C, iter: &mut Peekable) where I: Iterator, C: CardinalityManyCache { with_aev_iter(a, iter, |e, v| f.acc(e, v)) } fn accumulate_unique_evs_reverse(a: Entid, r: &mut UniqueReverseAttributeCache, iter: &mut Peekable) where I: Iterator { with_aev_iter(a, iter, |e, v| r.set(e, v)) } fn accumulate_non_unique_evs_reverse(a: Entid, r: &mut NonUniqueReverseAttributeCache, iter: &mut Peekable) where I: Iterator { with_aev_iter(a, iter, |e, v| r.acc(e, v)) } fn accumulate_single_val_unique_evs_both(a: Entid, f: &mut C, r: &mut UniqueReverseAttributeCache, iter: &mut Peekable) where I: Iterator, C: CardinalityOneCache { with_aev_iter(a, iter, |e, v| { f.set(e, v.clone()); r.set(e, v); }) } fn accumulate_multi_val_unique_evs_both(a: Entid, f: &mut C, r: &mut UniqueReverseAttributeCache, iter: &mut Peekable) where I: Iterator, C: CardinalityManyCache { with_aev_iter(a, iter, |e, v| { f.acc(e, v.clone()); r.set(e, v); }) } fn accumulate_single_val_non_unique_evs_both(a: Entid, f: &mut C, r: &mut NonUniqueReverseAttributeCache, iter: &mut Peekable) where I: Iterator, C: CardinalityOneCache { with_aev_iter(a, iter, |e, v| { f.set(e, v.clone()); r.acc(e, v); }) } fn accumulate_multi_val_non_unique_evs_both(a: Entid, f: &mut C, r: &mut NonUniqueReverseAttributeCache, iter: &mut Peekable) where I: Iterator, C: CardinalityManyCache { with_aev_iter(a, iter, |e, v| { f.acc(e, v.clone()); r.acc(e, v); }) } // TODO: if an entity or attribute is ever renumbered, the cache will need to be rebuilt. impl AttributeCaches { // // These function names are brief and local. // f = forward; r = reverse; both = both forward and reverse. // s = single-val; m = multi-val. // u = unique; nu = non-unique. // c = cache. #[inline] fn fsc(&mut self, a: Entid) -> &mut SingleValAttributeCache { self.single_vals .entry(a) .or_insert_with(Default::default) } #[inline] fn fmc(&mut self, a: Entid) -> &mut MultiValAttributeCache { self.multi_vals .entry(a) .or_insert_with(Default::default) } #[inline] fn ruc(&mut self, a: Entid) -> &mut UniqueReverseAttributeCache { self.unique_reverse .entry(a) .or_insert_with(Default::default) } #[inline] fn rnuc(&mut self, a: Entid) -> &mut NonUniqueReverseAttributeCache { self.non_unique_reverse .entry(a) .or_insert_with(Default::default) } #[inline] fn both_s_u<'r>(&'r mut self, a: Entid) -> (&'r mut SingleValAttributeCache, &'r mut UniqueReverseAttributeCache) { (self.single_vals.entry(a).or_insert_with(Default::default), self.unique_reverse.entry(a).or_insert_with(Default::default)) } #[inline] fn both_m_u<'r>(&'r mut self, a: Entid) -> (&'r mut MultiValAttributeCache, &'r mut UniqueReverseAttributeCache) { (self.multi_vals.entry(a).or_insert_with(Default::default), self.unique_reverse.entry(a).or_insert_with(Default::default)) } #[inline] fn both_s_nu<'r>(&'r mut self, a: Entid) -> (&'r mut SingleValAttributeCache, &'r mut NonUniqueReverseAttributeCache) { (self.single_vals.entry(a).or_insert_with(Default::default), self.non_unique_reverse.entry(a).or_insert_with(Default::default)) } #[inline] fn both_m_nu<'r>(&'r mut self, a: Entid) -> (&'r mut MultiValAttributeCache, &'r mut NonUniqueReverseAttributeCache) { (self.multi_vals.entry(a).or_insert_with(Default::default), self.non_unique_reverse.entry(a).or_insert_with(Default::default)) } // Process rows in `iter` that all share an attribute with the first. Leaves the iterator // advanced to the first non-matching row. fn accumulate_evs(&mut self, schema: &Schema, iter: &mut Peekable, replace_a: bool) where I: Iterator { if let Some(&(a, _, _)) = iter.peek() { if let Some(attribute) = schema.attribute_for_entid(a) { let forward = self.is_attribute_cached_forward(a); let reverse = self.is_attribute_cached_reverse(a); let multi = attribute.multival; let unique = attribute.unique.is_some(); match (forward, reverse, multi, unique) { (true, true, true, true) => { let (f, r) = self.both_m_u(a); if replace_a { f.clear(); r.clear(); } accumulate_multi_val_unique_evs_both(a, f, r, iter); }, (true, true, true, false) => { let (f, r) = self.both_m_nu(a); if replace_a { f.clear(); r.clear(); } accumulate_multi_val_non_unique_evs_both(a, f, r, iter); }, (true, true, false, true) => { let (f, r) = self.both_s_u(a); if replace_a { f.clear(); r.clear(); } accumulate_single_val_unique_evs_both(a, f, r, iter); }, (true, true, false, false) => { let (f, r) = self.both_s_nu(a); if replace_a { f.clear(); r.clear(); } accumulate_single_val_non_unique_evs_both(a, f, r, iter); }, (true, false, true, _) => { let f = self.fmc(a); if replace_a { f.clear(); } accumulate_multi_val_evs_forward(a, f, iter) }, (true, false, false, _) => { let f = self.fsc(a); if replace_a { f.clear(); } accumulate_single_val_evs_forward(a, f, iter) }, (false, true, _, true) => { let r = self.ruc(a); if replace_a { r.clear(); } accumulate_unique_evs_reverse(a, r, iter); }, (false, true, _, false) => { let r = self.rnuc(a); if replace_a { r.clear(); } accumulate_non_unique_evs_reverse(a, r, iter); }, (false, false, _, _) => { unreachable!(); // Must be cached in at least one direction! }, } } } } fn add_to_cache(&mut self, schema: &Schema, mut iter: Peekable, replace_a: bool) -> Result<()> where I: Iterator { while iter.peek().is_some() { self.accumulate_evs(schema, &mut iter, replace_a); } Ok(()) } fn clear_cache(&mut self) { self.single_vals.clear(); self.multi_vals.clear(); self.unique_reverse.clear(); self.non_unique_reverse.clear(); } fn unregister_all_attributes(&mut self) { self.reverse_cached_attributes.clear(); self.forward_cached_attributes.clear(); self.clear_cache(); } pub fn unregister_attribute(&mut self, attribute: U) where U: Into { let a = attribute.into(); self.reverse_cached_attributes.remove(&a); self.forward_cached_attributes.remove(&a); self.single_vals.remove(&a); self.multi_vals.remove(&a); self.unique_reverse.remove(&a); self.non_unique_reverse.remove(&a); } } impl CachedAttributes for AttributeCaches { fn get_values_for_entid(&self, schema: &Schema, attribute: Entid, entid: Entid) -> Option<&Vec> { self.values_pairs(schema, attribute) .and_then(|c| c.get(&entid)) } fn get_value_for_entid(&self, schema: &Schema, attribute: Entid, entid: Entid) -> Option<&TypedValue> { self.value_pairs(schema, attribute) .and_then(|c| c.get(&entid)) } fn is_attribute_cached_reverse(&self, attribute: Entid) -> bool { self.reverse_cached_attributes.contains(&attribute) } fn is_attribute_cached_forward(&self, attribute: Entid) -> bool { self.forward_cached_attributes.contains(&attribute) } fn get_entid_for_value(&self, attribute: Entid, value: &TypedValue) -> Option { if self.is_attribute_cached_reverse(attribute) { self.unique_reverse.get(&attribute).and_then(|c| c.get_e(value)) } else { None } } fn get_entids_for_value(&self, attribute: Entid, value: &TypedValue) -> Option<&BTreeSet> { if self.is_attribute_cached_reverse(attribute) { self.non_unique_reverse.get(&attribute).and_then(|c| c.get_es(value)) } else { None } } } impl AttributeCaches { fn values_pairs(&self, schema: &Schema, attribute: U) -> Option<&BTreeMap>> where U: Into { let attribute = attribute.into(); schema.attribute_for_entid(attribute) .and_then(|attr| if attr.multival { self.multi_vals .get(&attribute) .map(|c| &c.e_vs) } else { None }) } fn value_pairs(&self, schema: &Schema, attribute: U) -> Option<&CacheMap> where U: Into { let attribute = attribute.into(); schema.attribute_for_entid(attribute) .and_then(|attr| if attr.multival { None } else { self.single_vals .get(&attribute) .map(|c| &c.e_v) }) } } #[derive(Debug, Default)] pub struct SQLiteAttributeCache { inner: AttributeCaches, } impl SQLiteAttributeCache { pub fn register_forward(&mut self, schema: &Schema, sqlite: &rusqlite::Connection, attribute: U) -> Result<()> where U: Into { let a = attribute.into(); // The attribute must exist! let _ = schema.attribute_for_entid(a).ok_or_else(|| ErrorKind::UnknownAttribute(a))?; self.inner.forward_cached_attributes.insert(a); self.repopulate(schema, sqlite, a) } pub fn register_reverse(&mut self, schema: &Schema, sqlite: &rusqlite::Connection, attribute: U) -> Result<()> where U: Into { let a = attribute.into(); // The attribute must exist! let _ = schema.attribute_for_entid(a).ok_or_else(|| ErrorKind::UnknownAttribute(a))?; self.inner.reverse_cached_attributes.insert(a); self.repopulate(schema, sqlite, a) } pub fn register(&mut self, schema: &Schema, sqlite: &rusqlite::Connection, attribute: U) -> Result<()> where U: Into { let a = attribute.into(); // TODO: reverse-index unique by default? self.inner.forward_cached_attributes.insert(a); self.inner.reverse_cached_attributes.insert(a); self.repopulate(schema, sqlite, a) } fn repopulate(&mut self, schema: &Schema, sqlite: &rusqlite::Connection, attribute: Entid) -> Result<()> { let sql = "SELECT a, e, v, value_type_tag FROM datoms WHERE a = ? ORDER BY a ASC, e ASC"; let args: Vec<&rusqlite::types::ToSql> = vec![&attribute]; let mut stmt = sqlite.prepare(sql)?; let rows = stmt.query_map(&args, row_to_aev as fn(&rusqlite::Row) -> Aev)?; let aevs = AevRows { rows: rows, }; self.inner.add_to_cache(schema, aevs.peekable(), true)?; Ok(()) } pub fn unregister(&mut self, attribute: U) where U: Into { self.inner.unregister_attribute(attribute); } pub fn unregister_all(&mut self) { self.inner.unregister_all_attributes(); } } impl CachedAttributes for SQLiteAttributeCache { fn get_values_for_entid(&self, schema: &Schema, attribute: Entid, entid: Entid) -> Option<&Vec> { self.inner.get_values_for_entid(schema, attribute, entid) } fn get_value_for_entid(&self, schema: &Schema, attribute: Entid, entid: Entid) -> Option<&TypedValue> { self.inner.get_value_for_entid(schema, attribute, entid) } fn is_attribute_cached_reverse(&self, attribute: Entid) -> bool { self.inner.is_attribute_cached_reverse(attribute) } fn is_attribute_cached_forward(&self, attribute: Entid) -> bool { self.inner.is_attribute_cached_forward(attribute) } fn get_entids_for_value(&self, attribute: Entid, value: &TypedValue) -> Option<&BTreeSet> { self.inner.get_entids_for_value(attribute, value) } fn get_entid_for_value(&self, attribute: Entid, value: &TypedValue) -> Option { self.inner.get_entid_for_value(attribute, value) } } impl SQLiteAttributeCache { /// Intended for use from tests. pub fn values_pairs(&self, schema: &Schema, attribute: U) -> Option<&BTreeMap>> where U: Into { self.inner.values_pairs(schema, attribute) } /// Intended for use from tests. pub fn value_pairs(&self, schema: &Schema, attribute: U) -> Option<&BTreeMap> where U: Into { self.inner.value_pairs(schema, attribute) } }