![Nick Alexander](/assets/img/avatar_default.png)
This should address #663, by re-inserting type checking in the transactor stack after the entry point used by the term builder. Before this commit, we were using an SQLite UNIQUE index to assert that no `[e a]` pair, with `a` a cardinality one attribute, was asserted more than once. However, that's not in line with Datomic, which treats transaction inputs as a set and allows a single datom like `[e a v]` to appear multiple times. It's both awkward and not particularly efficient to look for _distinct_ repetitions in SQL, so we accept some runtime cost in order to check for repetitions in the transactor. This will allow us to address #532, which is really about whether we treat inputs as sets. A side benefit is that we can provide more helpful error messages when the transactor does detect that the input truly violates the cardinality constraints of the schema. This commit builds a trie while error checking and collecting final terms, which should be fairly efficient. It also allows a simpler expression of input-provided :db/txInstant datoms, which in turn uncovered a small issue with the transaction watcher, where-by the watcher would not see non-input-provided :db/txInstant datoms. This transition to Datomic-like input-as-set semantics allows us to address #532. Previously, two tempids that upserted to the same entid would produce duplicate datoms, and that would have been rejected by the transactor -- correctly, since we did not allow duplicate datoms under the input-as-list semantics. With input-as-set semantics, duplicate datoms are allowed; and that means that we must allow tempids to be equivalent, i.e., to resolve to the same tempid. To achieve this, we: - index the set of tempids - identify tempid indices that share an upsert - map tempids to a dense set of contiguous integer labels We use the well-known union-find algorithm, as implemented by petgraph, to efficiently manage the set of equivalent tempids. Along the way, I've fixed and added tests for two small errors in the transactor. First, don't drop datoms resolved by upsert (#679). Second, ensure that complex upserts are allocated. I don't know quite what happened here. The Clojure implementation correctly kept complex upserts that hadn't resolved as complex upserts (see9a9dfb502a/src/common/datomish/transact.cljc (L436)
) and then allocated complex upserts if they didn't resolve (see9a9dfb502a/src/common/datomish/transact.cljc (L509)
). Based on the code comments, I think the Rust implementation must have incorrectly tried to optimize by handling all complex upserts in at most a single generation of evolution, and that's just not correct. We're effectively implementing a topological sort, using very specific domain knowledge, and its not true that a node in a topological sort can be considered only once!
38 lines
644 B
TOML
38 lines
644 B
TOML
[package]
|
|
name = "mentat_db"
|
|
version = "0.0.1"
|
|
workspace = ".."
|
|
|
|
[dependencies]
|
|
error-chain = { git = "https://github.com/rnewman/error-chain", branch = "rnewman/sync" }
|
|
indexmap = "1"
|
|
itertools = "0.7"
|
|
lazy_static = "0.2"
|
|
log = "0.4"
|
|
num = "0.1"
|
|
ordered-float = "0.5"
|
|
time = "0.1"
|
|
petgraph = "0.4.12"
|
|
|
|
[dependencies.rusqlite]
|
|
version = "0.13"
|
|
features = ["limits"]
|
|
|
|
[dependencies.edn]
|
|
path = "../edn"
|
|
|
|
[dependencies.mentat_core]
|
|
path = "../core"
|
|
|
|
[dependencies.mentat_sql]
|
|
path = "../sql"
|
|
|
|
[dependencies.mentat_tx]
|
|
path = "../tx"
|
|
|
|
# Should be dev-dependencies.
|
|
[dependencies.tabwriter]
|
|
version = "1.0.3"
|
|
|
|
[dev-dependencies]
|
|
env_logger = "0.5"
|