diff --git a/db/src/db.rs b/db/src/db.rs index 33289878..7df3138e 100644 --- a/db/src/db.rs +++ b/db/src/db.rs @@ -1344,18 +1344,17 @@ mod tests { assert_transact!(conn, "[[:db/add :db/tx :db/txInstant #inst \"2017-06-16T00:59:11.257Z\"] [:db/add :db/tx :db/txInstant #inst \"2017-06-16T00:59:11.752Z\"] [:db/add 102 :db/ident :name/Vlad]]", - Err("Could not insert non-fts one statements into temporary search table!")); + Err("conflicting datoms in tx")); // Test multiple txInstants with the same value. - // Test disabled: depends on #535. - // assert_transact!(conn, "[[:db/add :db/tx :db/txInstant #inst \"2017-06-16T00:59:11.257Z\"] - // [:db/add :db/tx :db/txInstant #inst \"2017-06-16T00:59:11.257Z\"] - // [:db/add 103 :db/ident :name/Dimitri] - // [:db/add 104 :db/ident :name/Anton]]"); - // assert_matches!(conn.last_transaction(), - // "[[103 :db/ident :name/Dimitri ?tx true] - // [104 :db/ident :name/Anton ?tx true] - // [?tx :db/txInstant #inst \"2017-06-16T00:59:11.257Z\" ?tx true]]"); + assert_transact!(conn, "[[:db/add :db/tx :db/txInstant #inst \"2017-06-16T00:59:11.257Z\"] + [:db/add :db/tx :db/txInstant #inst \"2017-06-16T00:59:11.257Z\"] + [:db/add 103 :db/ident :name/Dimitri] + [:db/add 104 :db/ident :name/Anton]]"); + assert_matches!(conn.last_transaction(), + "[[103 :db/ident :name/Dimitri ?tx true] + [104 :db/ident :name/Anton ?tx true] + [?tx :db/txInstant #inst \"2017-06-16T00:59:11.257Z\" ?tx true]]"); // Test txInstant retraction // Test disabled: retracting a datom that doesn't exist should fail. diff --git a/db/src/errors.rs b/db/src/errors.rs index 62a263d0..15b8614c 100644 --- a/db/src/errors.rs +++ b/db/src/errors.rs @@ -82,5 +82,10 @@ error_chain! { description("unrecognized or no ident found for entid") display("unrecognized or no ident found for entid: {}", entid) } + + ConflictingDatoms { + description("conflicting datoms in tx") + display("conflicting datoms in tx") + } } } diff --git a/db/src/lib.rs b/db/src/lib.rs index f9416319..2c9ca700 100644 --- a/db/src/lib.rs +++ b/db/src/lib.rs @@ -8,6 +8,9 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. +// Oh, error_chain. +#![recursion_limit="128"] + #[macro_use] extern crate error_chain; extern crate itertools; diff --git a/db/src/tx.rs b/db/src/tx.rs index 45d11e6d..8adac2a5 100644 --- a/db/src/tx.rs +++ b/db/src/tx.rs @@ -633,11 +633,21 @@ impl<'conn, 'a> Tx<'conn, 'a> { let added = op == OpType::Add; // We take the last encountered :db/txInstant value. + // If more than one is provided, the transactor will fail. if added && e == self.tx_id && a == entids::DB_TX_INSTANT { if let TypedValue::Instant(instant) = v { - self.tx_instant = Some(instant); + if let Some(ts) = self.tx_instant { + if ts == instant { + // Dupes are fine. + } else { + bail!(ErrorKind::ConflictingDatoms); + } + } else { + self.tx_instant = Some(instant); + } + continue; } else { // The type error has been caught earlier. unreachable!() @@ -657,14 +667,12 @@ impl<'conn, 'a> Tx<'conn, 'a> { tx_instant = self.tx_instant.unwrap_or_else(now); - // Transact [:db/add :db/txInstant NOW :db/tx] if it doesn't exist. - if self.tx_instant == None { - non_fts_one.push((self.tx_id, - entids::DB_TX_INSTANT, - self.schema.require_attribute_for_entid(entids::DB_TX_INSTANT).unwrap(), - TypedValue::Instant(tx_instant), - true)); - } + // Transact [:db/add :db/txInstant tx_instant :db/tx]. + non_fts_one.push((self.tx_id, + entids::DB_TX_INSTANT, + self.schema.require_attribute_for_entid(entids::DB_TX_INSTANT).unwrap(), + tx_instant.into(), + true)); if !non_fts_one.is_empty() { self.store.insert_non_fts_searches(&non_fts_one[..], db::SearchType::Inexact)?;