Commit graph

278 commits

Author SHA1 Message Date
Nick Alexander
71446a3af5 Completely rewrite main transaction logic to be faster.
This is almost complete; it passes the test suite save for retracting
fulltext datoms correctly.

There's a lot to say about this approach, but I don't have time to give
too many details.  The broad outline is as follows.  We collect datoms
to add and retract in a tx_lookup table.  Depending on flags ("search
value" sv and "search value type tag" svalue_type_tag) we "complete" the
tx_lookup table by joining matching datoms.  This allows us to find
datoms that are present (and should not be added as part of the
transaction, or should be retracted as part of the transaction, or
should be replaced as part of the transaction.  We complete the
tx_lookup (in place!) in two separate INSERTs to avoid a quadratic
two-table walk (explain the queries to observe that both INSERTs walk
the lookup table once and then use the datoms indexes to complete the
matching values).

We could simplify the code by using multiple lookup tables, both for the
two cases of search parameters (eav vs. ea) and for the incomplete and
completed rows.  Right now we differentiate the former with NULL checks,
and the latter by incrementing the added0 column.  It performs well
enough, so I haven't tried to understand the performance of separating
these things.

After the tx_lookup table is completed, we build the transaction from
it; and update the datoms materialized view table as well.  Observe the
careful handling of the "search value" sv parameters to handle replacing
:db.cardinality/one datoms.

Finally, we read the processed transaction back to produce to the API.
This is strictly to match the Datomic API; we might make allow to skip
this, since many consumers will not want to stream this over the wire.

Rough timings show the transactor processing a single >50k datom
transaction in about 3.5s, of which less than 0.5s is spent in the
expensive joins.  Further, repeating the processing of the same
transaction is only about 3.5s again!  That's the worst possible for the
joins, since every single inserted datom will already be present in the
database, making the most expensive join match every row.
2016-08-30 18:22:10 -07:00
Nick Alexander
b59c71cb00 Extract datomish.db.debug from test code, in order to use it during development. 2016-08-30 18:22:10 -07:00
Nick Alexander
a1cc372d43 Fix :db.unique/value, which should be per (a, v) pair, not per v-value. 2016-08-30 18:22:10 -07:00
Nick Alexander
1b1cc6f18e Add Datomic, for testing. 2016-08-30 18:22:09 -07:00
Nick Alexander
b29e5caec0 Implement parts: Make the DB allocate and persist entity IDs.
This implementation is inefficient because each allocated temporary ID
touches the database, but it's enough to allow to re-open DBs.
2016-08-30 18:22:09 -07:00
Richard Newman
22fc2ce201 Define <-tagged-SQLite and tagged-SQLite-to-JS to do tag-aware value transforms. 2016-08-30 18:22:09 -07:00
Nick Alexander
ff9a75ae09 Tag values with value type tags in SQLite. 2016-08-30 18:22:09 -07:00
Nick Alexander
4d34c820b8 Add d/q; make query minimally schema aware. 2016-08-30 18:22:09 -07:00
Nick Alexander
0f7c1cad79 Extract IEncodeSQLite protocol and type-aware (but not schema-aware) <-SQLite factory. 2016-08-30 18:22:09 -07:00
Richard Newman
655a6a1461 Raise a specific error when trying to transact invalid sequences.
This specifically checks for things like :db/add foo bar (nil),
which will otherwise fail elsewhere after being exploded.
2016-08-30 18:22:09 -07:00
Nick Alexander
e7e84e0a90 Add d/{ident,entid} for mapping between keyword idents and integer entids. 2016-08-30 18:22:09 -07:00
Richard Newman
dc87d7d557 Correctly handle SQL errors in <?all-rows. Fixes #40. 2016-08-30 18:22:09 -07:00
Richard Newman
14a6a9bebb Configure our JDBC interface to not downcase column names in result sets. 2016-08-30 18:22:09 -07:00
Richard Newman
cf96bf1888 Always use our sql-quoting-style. 2016-08-30 18:22:09 -07:00
Richard Newman
cff49b6df5 Initialize the sqlite connection with WAL and foreign keys.
This somewhat improves performance, which is nice.
2016-08-30 18:22:09 -07:00
Richard Newman
1a54cec1ba Use correct view for fulltext+non-fulltext. 2016-08-05 16:29:21 -07:00
Richard Newman
b1e6ffc91c Example query hacking. 2016-08-05 13:04:09 -07:00
Richard Newman
fc845a9950 Implement basic fulltext binding. r=nalexander 2016-08-05 13:04:06 -07:00
Richard Newman
57d8796d07 Split out datomish.query.cc so we can implement clause resolution in more than one namespace. r=nalexander 2016-08-05 13:04:01 -07:00
Richard Newman
fc65f268fa Process Function instances in clause handling. r=nalexander 2016-08-05 13:03:52 -07:00
Richard Newman
4c2230929e Turn Source into a protocol. Allow source->from to switch on attribute. r=nalexander 2016-08-05 13:03:48 -07:00
Nick Alexander
c948902c52 Follow-up: Extract datomish.transact.explode.
This required pushing the ID literal out of transact.  I elected to put
them in DB, since literal allocation will end up in IDB eventually.
2016-08-04 17:54:50 -07:00
Nick Alexander
d8c976c3ad Follow-up: split the monolith!
This was a little more tricky than might be expected because the
initialization process uses the transactor to bootstrap the database.
Since Clojure doesn't accept mutually recursive modules, this
necessitated a third module, namely "db-factory", which uses both "db"
and "transact".  While I was here, I started an "api" module, to paper
over the potentially complicated internal module structure for external
consumers.  In time, this "api" module may also grow CLJS-specific JS
transformations.
2016-08-04 17:54:32 -07:00
Nick Alexander
1853d57cba Follow-up: extract datomish.transact.bootstrap. 2016-08-04 16:37:14 -07:00
Nick Alexander
73b155cfdc Follow-up: don't accept schema when creating DB.
This schema was already ignored.
2016-08-04 16:34:48 -07:00
Nick Alexander
52af06ce28 Follow-up: lift in-transaction! to DB; make transactions exclusive. 2016-08-04 16:27:36 -07:00
Nick Alexander
f408cfd314 Follow-up: lift helpers to IDB. 2016-08-04 16:20:48 -07:00
Nick Alexander
65d71a2598 Review comment: use :quoting :ansi parameter to honeysql/format. 2016-08-04 15:42:25 -07:00
Nick Alexander
9c2d81b9c4 Review comments: update SQLite schema definitions. 2016-08-04 15:42:25 -07:00
Nick Alexander
6a01885dff Review comment: simplify equality. 2016-08-04 14:51:17 -07:00
Nick Alexander
c344de665e Review comments: lift (schema db) in a few places. 2016-08-04 14:50:05 -07:00
Nick Alexander
5ece60e3e9 Review comment: be more strict in lookup-ref?. 2016-08-04 14:45:41 -07:00
Nick Alexander
44db8116bf Handle nested maps, sequences in maps, and reverse references. 2016-08-04 14:26:20 -07:00
Nick Alexander
d9a8cb0d6a Don't accept user-provided tx values.
This agrees with Datomic.  DataScript allows tx values, possibly to
allow reconstructing DBs from Datom streams, but appears to handle
user-provided tx values in the transactor inconsistently.
2016-08-04 14:26:20 -07:00
Nick Alexander
38545f6efc Add :tx and :txInstant to TxReport; accept :db/tx in transactor; allow to set :db/txInstant.
The implementation of :db/tx is special and may need to change over
time.  We add it as a special ident, with value the current transaction
entity ID, specified per-transaction.  This works well right now but
introduces some (internal) ordering requirements that may need to be
loosened.
2016-08-04 14:26:20 -07:00
Nick Alexander
417ae1ed92 Allow false; fail if too many components in vector given. 2016-08-04 14:26:20 -07:00
Nick Alexander
f25838a1eb Support :db/fulltext true.
Internally, we use SQLite's FTS4 to maintain a fulltext_values table of
unique "text" values.  Fulltext indexed datoms have value v that is the
rowid into fulltext_values.  We manually maintain the map between rowid
and value in the transactor.

For convenience, we expose two views interpolating the real text values
into the datoms structure.
2016-08-04 14:26:20 -07:00
Nick Alexander
296c9cb436 Bootstrap DB schema; persist and restore schema from materialized views. 2016-08-04 14:26:20 -07:00
Nick Alexander
5d271454ac Don't replace keyword idents if the underlying field is itself :db.type/keyword.
This is necessary to assert new :db/ident datoms.
2016-08-04 14:26:20 -07:00
Nick Alexander
a1e3158eac Add some structure and value checking. 2016-08-04 14:26:20 -07:00
Nick Alexander
6a8739bd2f Accept new schema fragments via :db.part/db :db.install/attribute. 2016-08-04 14:26:20 -07:00
Nick Alexander
9497d69b44 Respect :db/unique constraints; test upserts.
This version includes SQLite-level unique indexes; these should never be
needed.  I've included them as a fail-safe while testing; they'll help
us catch errors in the transaction layer above.
2016-08-04 14:26:20 -07:00
Nick Alexander
80742242e2 Fix cljs 2016-08-04 14:26:20 -07:00
Nick Alexander
7a90c43a5a Map valueTypes to SQLite encodings.
In the future, we might add a layer of indirection, hashing values to
avoid duplicating storage, or sorting URLs, or handling fulltext indexed
values differently, or ...
2016-08-04 14:26:20 -07:00
Nick Alexander
43423b7d0a Validate value types. 2016-08-04 14:26:20 -07:00
Nick Alexander
bceee3b5fb Re-throw errors in Clojure, for easier debugging. 2016-08-04 14:26:20 -07:00
Nick Alexander
661e7ed123 Allow to add new :db/ident mappings. 2016-08-04 14:26:20 -07:00
Nick Alexander
fbd5863921 Resolve lookup-refs. 2016-08-04 14:26:20 -07:00
Nick Alexander
baec3815b0 Implement transactions. 2016-08-04 14:26:20 -07:00
Nick Alexander
0c51cb6236 Pre: Catch Throwable, not Exception. 2016-08-04 14:26:20 -07:00
Richard Newman
8a77dcd8f0 Implement simple 'or' clauses. r=nalexander 2016-07-27 17:04:32 -07:00
Richard Newman
1ad67a03eb Add tests and comments for clause ordering. 2016-07-26 11:19:51 -07:00
Richard Newman
a5556637e8 Add a table alias function to Source, allowing us to mock alias generation. 2016-07-26 10:50:40 -07:00
Richard Newman
5e1648b05f Remove a duplicate definition of context->sql-string. 2016-07-26 10:50:10 -07:00
Richard Newman
8633efc6be Fix exec-repl to build in cljs. 2016-07-26 10:49:43 -07:00
Richard Newman
d9cff5fa3c Hacking on exec-repl. 2016-07-25 17:08:53 -07:00
Richard Newman
42361c1e5e Implement negation, predicates, external scalar bindings, <?q. r=nalexander 2016-07-25 17:08:00 -07:00
Nick Alexander
9ae9a0572b Pass a Context into query. 2016-07-19 10:35:39 -07:00
Nick Alexander
f4b9b867a4 Indenting. 2016-07-19 10:35:39 -07:00
Nick Alexander
4262b10f55 Handle empty :where SQL clauses in query. 2016-07-19 10:35:39 -07:00
Nick Alexander
13dc1cc9b6 Take more general raise from DataScript. 2016-07-19 10:35:35 -07:00
Richard Newman
2a31a0c4d4 Fix error in pair-channel->lazy-seq.
Signed-off-by: Richard Newman <rnewman@twinql.com>
2016-07-15 12:37:01 -07:00
Richard Newman
e76895ac00 Generalize test code for <?run to work in either environment.
Signed-off-by: Richard Newman <rnewman@twinql.com>
2016-07-15 12:36:46 -07:00
Richard Newman
d695554123 Initial work on executing queries. r=nalexander
Signed-off-by: Richard Newman <rnewman@twinql.com>
2016-07-15 09:01:44 -07:00
Richard Newman
437a80a978 Store elements on the context. 2016-07-15 09:01:44 -07:00
Richard Newman
64460f7eef eavt -> datoms.
Signed-off-by: Richard Newman <rnewman@twinql.com>
2016-07-15 09:01:44 -07:00
Richard Newman
6c367616e6 Add while-let.
Signed-off-by: Richard Newman <rnewman@twinql.com>
2016-07-15 08:58:12 -07:00
Nick Alexander
bf080ced3c Part 2: Add ISQLiteConnectionFactory and CLJC test that uses it. 2016-07-13 18:19:22 -07:00
Nick Alexander
cca5010671 Part 1: Fix testing errors.
Some of these were just typos, but `with-open` was fatally flawed on
CLJS (we couldn't call `.close` at all), and `deftest-async` was hiding
all failures (due to a typo).
2016-07-13 18:19:22 -07:00
Nick Alexander
900e77862e Implement database on top of SQLite connection. r=rnewman
We would prefer to talk about a knowledge base on top of a database, but
all the Datomic and DataScript code (and symbols, like :db/add, etc)
refer to the "database of datoms", so let's roll with that nomenclature
and try to be specific that the persistent storage-layer is SQLite.
This will become more clear when we actually use SQLite's unique
capabilities for text indexing.
2016-07-13 12:37:23 -07:00
Nick Alexander
636b7a7957 Pre: Delete trailing whitespace. 2016-07-13 12:04:29 -07:00
Richard Newman
db68a714f6 First pass at translating Datalog queries into SQL. r=nalexander
Signed-off-by: Richard Newman <rnewman@twinql.com>
2016-07-13 09:51:42 -07:00
Nick Alexander
724c37466d Add an SQLite connection abstraction. 2016-07-12 13:56:26 -07:00
Nick Alexander
d42e2f02a6 Expose with-open to CLJS. 2016-07-12 13:56:26 -07:00
Nick Alexander
d5cfbeaa45 Add tempfile and with-tempfile to CLJS.
We already have a nice library like this for CLJ (tempfile); this builds
the same thing for CLJS, using Node.js's tmp.
2016-07-12 13:56:26 -07:00
Nick Alexander
0a312b4f40 Add an async and async testing framework.
This is a well-worn idea: use a `promise-channel` of `[result nil]` or
`[nil error]` pairs.  The `go-pair` and `<?` macros handle catching
exceptions (important, given that synchronous CLJ code expects to throw
rather than return an error promise or similar), allowing code like:
```
(go-pair
  (let [result (<? (pair-chan-fn))]
    (when (not result)
      (throw (Exception. "No result!")))
    (transform result)))
```
to be expressed naturally.  These are the equivalents of `async` and
`await` in JS.

The implementation is complicated by significant incompatibilities
between CLJ and CLJS.  The solution presented here takes care to
separate the macro definitions into CLJ.  Sadly, this requires
namespacing the per-environment symbols explicitly; but we hope to
minimize such code in files like this.

The most significant restriction to this approach is that consumers must
require the transitive dependencies of the macro-defining modules.  See
the included tests (both CLJ and CLJS) for the appropriate
incantations (for pair-chan, core.async, and test).
2016-07-12 13:56:26 -07:00
Nick Alexander
f2365646d2 Add lein-cljsbuild and adapt datascript's release-js vehicle. 2016-07-06 16:42:37 -07:00
Richard Newman
9e3df19798 Stub code, build, and REPL.
Signed-off-by: Richard Newman <rnewman@twinql.com>
2016-07-05 12:36:55 -07:00