Principally, this adds producing libmentat_ffi.a for consumption by Carthage.
To achieve this, we disable bitcode and bump the miniumum iOS version
to 11. In addition, we make things open and public so that consumers
can, well, consume.
This is a big deck-chair re-arrangement. This puts FindQuery into
query-algebrizer and puts the validation from ParsedFindQuery ->
FindQuery their as well.
Some tests were re-homed for this.
In addition, the little-used maplit crate dependency was replaced with
inline expressions.
There's an unfortunate conflation here between implementing the query
parser in `rust-peg` and moving some validation that now happens at
parse time to happen later. The result is that we introduce
`ParsedFindQuery` as a less-processed `FindQuery`, and that we only
use string errors (which is all `rust-peg` supports) instead of the
structured errors in query-parser's errors module. The next commit
will address this, on the road to removing the `query-parser` module
entirely.
This is a pre-requisite for moving the existing `combine`-based parser
to use `rust-peg` -- part of the push to use `rust-peg` for all parsing
started in https://github.com/mozilla/mentat/pull/681. We need the
types for the parsed structure "very early", and the `edn` crate is
the earliest such crate.
This is an unfortunate destruction of boundaries between parts of the
system, but it's the best way we have to achieve this right now.
This is a style choice. We supported both, perhaps for Datomic
compliance, but it's not the standard we use in our code base. In
addition, it doesn't read like lisp (which is what EDN is copying),
since [] is not function application in most lisps.
It's also a convenience: I don't want to parse brackets that have to
agree with `rust-peg`. It's not hard but it's also not worth doing.
The `Keyword` type evolved to become more general: we now use the one
type for both :regular and :name/spaced keywords. This changes
reflects the new generality.
This uses a `SELECT *` from an inner subselect to filter potentially `NULL` aggregates.
The alternative is to handle `NULL` values throughout the projector, which is simple but loses a valuable invariant: Mentat SQL queries produce values that are not `NULL`.
* Add an IntelliJ section to gitignore
* Add Android SDK sample project which exercises mentat SDK
* Symlink libmentat_ffi.so in Android SDK to the generated --release files
* README files for Android SDK and mentat_ffi
* Include the namespace-separating solidus in NamespaceableName.
* Use type annotations when deciding how to process ambiguous ground input.
* Include simple patterns in the type extraction phase of pattern application. (#705)
* Review comment.
* Add a test.
`CacheDirection` enum is used only on the Android side to provide a usable interface. FFI calls are more explicit.
Tests ensure that a cached query is faster than the uncached one.
`CacheDirection` enum is used only on the Swift side to provide a usable interface. FFI calls are more explicit.
Tests ensure that a cached query is faster than the uncached one.
This exposes an FFI function for each direction of caching, `Forward`, `Reverse` and `Both`. This is to make is as clear as possible to consumers which direction they are caching their attributes in. The original implementation exposed the `CacheDirection` enum over FFI and it made mistakes very easy to make. This is more explicit and therefore less prone to error.
There are two ways to create each builder, directly from a `Store` or from an `InProgress`. Creating from `Store` will perform two actions, creating a new `InProgress` and then returning a builder from that `InProgress`. In the case of `store_entity_builder_with_entid` and `store_entity_builder_from_tempid`, the function goes a step further and calls `describe` or `describe_tempid` from the created `InProgressBuilder` and returning the `EntityBuilder` that results. These two functions are replicated on `InProgress`. This has been done to reduce the overhead of objects being passed over the FFI boundary.
The decision to do this enables us to go from something like
```
in_progress = store_begin_transaction(store);
builder = in_progress_builder(in_progress);
entity_builder = in_progress_builder_describe(builder, entid);
```
to
```
entity_builder = store_entity_builder_from_entid(store);
```
There is an `add_*` and `retract_*` function specified for each `TypedValue` type for both `InProgressBuilder` and `EntityBuilder`.
To enable `transact` on `EntityBuilder` and `InProgressBuilder`, a new `repr(C)` struct has been created that contains a pointer to an `InProgress` and a pointer to a `Result<TxReport>` to allow passing the tuple result returned from `transact` on those types over the FFI.
Commit is possible from both builders and `InProgress`.
* Part 3: Parameterize Entity by value type.
This isn't quite right, because after parsing, we shouldn't care
about` `edn::ValueAndSpan`, we should care only about edn::Value.
However, I think we can drop `ValueAndSpan` entirely if we just use
`rust-peg` (and its simpler error messages) rather than a mix of
`rust-peg` and `combine`.
In any case, this paves the way to transacting `Entity<TypedValue>`,
which is a nice step towards building general entities.
* Part 1: Add AttributePlace.
* Part 2: Name other places EntityPlace and ValuePlace.
Now we're consistent and closer to self-documenting. Both matter more
as we expose `Entity` as the thing to build for programmatic usage.
* Part 4: Allow Ident and TempId in ValuePlace.
The parser will never produce these, since determining whether an
integer/keyword or string is an ident or a tempid, respectively, in
the value place requires the schema.
But a builder that produces `Entity` instances directly will want to
produce these.
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 (see
9a9dfb502a/src/common/datomish/transact.cljc (L436))
and then allocated complex upserts if they didn't resolve (see
9a9dfb502a/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!