Skip to content
This repository was archived by the owner on Sep 12, 2018. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
77266c3
Pre: s/VALUETYPE/VALUE_TYPE/.
ncalexan Mar 10, 2017
fed8dfd
Pre: Don't retract :db/ident in test.
ncalexan Mar 10, 2017
c0d3ee7
Pre: Remove some single quotes from error output.
ncalexan Mar 10, 2017
c708ae7
Pre: Map TypedValue::Ref to TypedValue::Keyword in debug output.
ncalexan Mar 10, 2017
057c45b
Pre: Add DiffSet.
ncalexan Mar 14, 2017
19861c0
Part 1: Make materialized views be uniform [e a v value_type_tag].
ncalexan Mar 10, 2017
a2cd37a
Part 2: Maintain entids separately from idents.
ncalexan Mar 14, 2017
63dcff8
Part 3: Separate `schema_to_mutate` from the `schema` used to interpret.
ncalexan Mar 13, 2017
91ad2d8
Part 4: Collect mutations to a `Schema`.
ncalexan Mar 15, 2017
29c6a33
Part 5: Handle :db/ident and :db.{install,alter}/attribute.
ncalexan Mar 15, 2017
36633c1
Review comment: Rename DiffSet to AddRetractAlterSet.
ncalexan Mar 15, 2017
8483fd9
Review comment: Use ToIdent trait.
ncalexan Mar 15, 2017
6c34d83
Review comment: partially revert "Part 2: Maintain entids separately …
ncalexan Mar 15, 2017
6172581
Review comment: Don't preserve historical idents.
ncalexan Mar 15, 2017
114d205
Review comment: More prepared statements when updating materialized v…
ncalexan Mar 15, 2017
5203ebb
Post: Use custom Either rather than std::result::Result.
ncalexan Mar 15, 2017
37816b5
Post: Test altering :db/cardinality and :db/unique.
ncalexan Mar 15, 2017
add8144
Post: Remove some more single quotes from error output.
ncalexan Mar 16, 2017
b93e059
Post: Add assert_transact! macro to unwrap safely.
ncalexan Mar 16, 2017
a8015da
Post: Don't expect or recognize :db.{install,alter}/attribute.
ncalexan Mar 16, 2017
c0b75b9
Post: Don't make :db/unique :db.unique/* imply :db/index true.
ncalexan Mar 16, 2017
edac729
Post: Allow to retract :db/ident.
ncalexan Mar 16, 2017
2b39f02
Post: Include more details about invalid schema changes.
ncalexan Mar 17, 2017
baedbb8
Review comment: Fix outdated comment.
ncalexan Mar 20, 2017
99c7217
Review comment: s/_SET/_SQL_LIST/.
ncalexan Mar 20, 2017
0ff0257
Review comment: Use a sub-select for checking cardinality.
ncalexan Mar 20, 2017
704b0a7
Review comment: Put `attribute::Unique` into its own namespace.
ncalexan Mar 20, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 35 additions & 14 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ impl TypedValue {
&TypedValue::Keyword(_) => ValueType::Keyword,
}
}

}

// Put this here rather than in `db` simply because it's widely needed.
Expand Down Expand Up @@ -139,6 +138,14 @@ pub enum AttributeBitFlags {
UniqueValue = 1 << 3,
}

pub mod attribute {
#[derive(Clone,Debug,Eq,Hash,Ord,PartialOrd,PartialEq)]
pub enum Unique {
Value,
Identity,
}
}

/// A Mentat schema attribute has a value type and several other flags determining how assertions
/// with the attribute are interpreted.
///
Expand All @@ -153,19 +160,22 @@ pub struct Attribute {
/// is `:db/cardinality :db.cardinality/one`.
pub multival: bool,

/// `true` if this attribute is unique-value, i.e., it is `:db/unique :db.unique/value`.
/// `None` if this attribute is neither unique-value nor unique-identity.
///
/// `Some(attribute::Unique::Value)` if this attribute is unique-value, i.e., it is `:db/unique
/// :db.unique/value`.
///
/// *Unique-value* means that there is at most one assertion with the attribute and a
/// particular value in the datom store.
pub unique_value: bool,

/// `true` if this attribute is unique-identity, i.e., it is `:db/unique :db.unique/identity`.
/// particular value in the datom store. Unique-value attributes can be used in lookup-refs.
///
/// `Some(attribute::Unique::Identity)` if this attribute is unique-identity, i.e., it is `:db/unique
/// :db.unique/identity`.
///
/// Unique-identity attributes always have value type `Ref`.
///
/// *Unique-identity* means that the attribute is *unique-value* and that they can be used in
/// lookup-refs and will automatically upsert where appropriate.
pub unique_identity: bool,
pub unique: Option<attribute::Unique>,

/// `true` if this attribute is automatically indexed, i.e., it is `:db/indexing true`.
pub index: bool,
Expand Down Expand Up @@ -198,7 +208,7 @@ impl Attribute {
if self.fulltext {
flags |= AttributeBitFlags::IndexFulltext as u8;
}
if self.unique_value {
if self.unique.is_some() {
flags |= AttributeBitFlags::UniqueValue as u8;
}
flags
Expand All @@ -213,8 +223,7 @@ impl Default for Attribute {
fulltext: false,
index: false,
multival: false,
unique_value: false,
unique_identity: false,
unique: None,
component: false,
}
}
Expand Down Expand Up @@ -294,9 +303,8 @@ mod test {
index: true,
value_type: ValueType::Ref,
fulltext: false,
unique_value: false,
unique: None,
multival: false,
unique_identity: false,
component: false,
};

Expand All @@ -309,16 +317,29 @@ mod test {
index: false,
value_type: ValueType::Boolean,
fulltext: true,
unique_value: true,
unique: Some(attribute::Unique::Value),
multival: false,
unique_identity: false,
component: false,
};

assert!(attr2.flags() & AttributeBitFlags::IndexAVET as u8 == 0);
assert!(attr2.flags() & AttributeBitFlags::IndexVAET as u8 == 0);
assert!(attr2.flags() & AttributeBitFlags::IndexFulltext as u8 != 0);
assert!(attr2.flags() & AttributeBitFlags::UniqueValue as u8 != 0);

let attr3 = Attribute {
index: false,
value_type: ValueType::Boolean,
fulltext: true,
unique: Some(attribute::Unique::Identity),
multival: false,
component: false,
};

assert!(attr3.flags() & AttributeBitFlags::IndexAVET as u8 == 0);
assert!(attr3.flags() & AttributeBitFlags::IndexVAET as u8 == 0);
assert!(attr3.flags() & AttributeBitFlags::IndexFulltext as u8 != 0);
assert!(attr3.flags() & AttributeBitFlags::UniqueValue as u8 != 0);
}
}

Expand Down
85 changes: 85 additions & 0 deletions db/src/add_retract_alter_set.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// 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.

#![allow(dead_code)]

use std::collections::BTreeMap;

/// Witness assertions and retractions, folding (assertion, retraction) pairs into alterations.
/// Assumes that no assertion or retraction will be witnessed more than once.
///
/// This keeps track of when we see a :db/add, a :db/retract, or both :db/add and :db/retract in
/// some order.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub struct AddRetractAlterSet<K, V> {
pub asserted: BTreeMap<K, V>,
pub retracted: BTreeMap<K, V>,
pub altered: BTreeMap<K, (V, V)>,
}

impl<K, V> Default for AddRetractAlterSet<K, V> where K: Ord {
fn default() -> AddRetractAlterSet<K, V> {
AddRetractAlterSet {
asserted: BTreeMap::default(),
retracted: BTreeMap::default(),
altered: BTreeMap::default(),
}
}
}

impl<K, V> AddRetractAlterSet<K, V> where K: Ord {
pub fn witness(&mut self, key: K, value: V, added: bool) {
if added {
if let Some(retracted_value) = self.retracted.remove(&key) {
self.altered.insert(key, (retracted_value, value));
} else {
self.asserted.insert(key, value);
}
} else {
if let Some(asserted_value) = self.asserted.remove(&key) {
self.altered.insert(key, (value, asserted_value));
} else {
self.retracted.insert(key, value);
}
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test() {
let mut set: AddRetractAlterSet<i64, char> = AddRetractAlterSet::default();
// Assertion.
set.witness(1, 'a', true);
// Retraction.
set.witness(2, 'b', false);
// Alteration.
set.witness(3, 'c', true);
set.witness(3, 'd', false);
// Alteration, witnessed in the with the retraction before the assertion.
set.witness(4, 'e', false);
set.witness(4, 'f', true);

let mut asserted = BTreeMap::default();
asserted.insert(1, 'a');
let mut retracted = BTreeMap::default();
retracted.insert(2, 'b');
let mut altered = BTreeMap::default();
altered.insert(3, ('d', 'c'));
altered.insert(4, ('e', 'f'));

assert_eq!(set.asserted, asserted);
assert_eq!(set.retracted, retracted);
assert_eq!(set.altered, altered);
}
}
13 changes: 5 additions & 8 deletions db/src/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ lazy_static! {
(ns_keyword!("db.part", "db"), entids::DB_PART_DB),
(ns_keyword!("db", "txInstant"), entids::DB_TX_INSTANT),
(ns_keyword!("db.install", "partition"), entids::DB_INSTALL_PARTITION),
(ns_keyword!("db.install", "valueType"), entids::DB_INSTALL_VALUETYPE),
(ns_keyword!("db.install", "valueType"), entids::DB_INSTALL_VALUE_TYPE),
(ns_keyword!("db.install", "attribute"), entids::DB_INSTALL_ATTRIBUTE),
(ns_keyword!("db", "valueType"), entids::DB_VALUE_TYPE),
(ns_keyword!("db", "cardinality"), entids::DB_CARDINALITY),
Expand Down Expand Up @@ -97,6 +97,7 @@ lazy_static! {
let s = r#"
{:db/ident {:db/valueType :db.type/keyword
:db/cardinality :db.cardinality/one
:db/index true
:db/unique :db.unique/identity}
:db.install/partition {:db/valueType :db.type/ref
:db/cardinality :db.cardinality/many}
Expand Down Expand Up @@ -142,6 +143,7 @@ lazy_static! {
;; unique-value because an attribute can only belong to a single
;; schema fragment.
:db.schema/attribute {:db/valueType :db.type/ref
:db/index true
:db/unique :db.unique/value
:db/cardinality :db.cardinality/many}}"#;
let right = edn::parse::value(s)
Expand Down Expand Up @@ -169,8 +171,8 @@ fn idents_to_assertions(idents: &[(symbols::NamespacedKeyword, i64)]) -> Vec<Val
/// 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
/// :db.install/attribute assertions.
/// Such triples are closer to what the transactor will produce when processing attribute
/// assertions.
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.
let mut triples: Vec<(symbols::NamespacedKeyword, symbols::NamespacedKeyword, TypedValue)> = vec![];
Expand Down Expand Up @@ -221,17 +223,12 @@ fn symbolic_schema_to_triples(ident_map: &IdentMap, symbolic_schema: &Value) ->
}

/// Convert {IDENT {:key :value ...} ...} to [[:db/add IDENT :key :value] ...].
/// In addition, add [:db.add :db.part/db :db.install/attribute IDENT] installation assertions.
fn symbolic_schema_to_assertions(symbolic_schema: &Value) -> Result<Vec<Value>> {
// Failure here is a coding error, not a runtime error.
let mut assertions: Vec<Value> = vec![];
match *symbolic_schema {
Value::Map(ref m) => {
for (ident, mp) in m {
assertions.push(Value::Vector(vec![values::DB_ADD.clone(),
values::DB_PART_DB.clone(),
values::DB_INSTALL_ATTRIBUTE.clone(),
ident.clone()]));
match *mp {
Value::Map(ref mpp) => {
for (attr, value) in mpp {
Expand Down
Loading