From e7e84e0a908ef6c03ea3ea25399cd60aeaa8d72b Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Fri, 5 Aug 2016 13:59:07 -0700 Subject: [PATCH] Add d/{ident,entid} for mapping between keyword idents and integer entids. --- src/datomish/db.cljc | 70 +++++++++++++++++++++++------- src/datomish/db_factory.cljc | 14 +----- src/datomish/schema_changes.cljc | 3 -- src/datomish/transact.cljc | 44 ++++++++----------- src/datomish/transact/explode.cljc | 10 ++--- test/datomish/api.cljc | 4 ++ test/datomish/db_test.cljc | 50 ++++++++++----------- 7 files changed, 107 insertions(+), 88 deletions(-) diff --git a/src/datomish/db.cljc b/src/datomish/db.cljc index 12c4f34e..aaf0259d 100644 --- a/src/datomish/db.cljc +++ b/src/datomish/db.cljc @@ -8,6 +8,7 @@ [datomish.pair-chan :refer [go-pair entid} of known idents. See http://docs.datomic.com/identity.html#idents. +(defrecord DB [sqlite-connection schema entids ident-map current-tx] + ;; ident-map maps between keyword idents and integer entids. The set of idents and entids is + ;; disjoint, so we represent both directions of the mapping in the same map for simplicity. Also + ;; for simplicity, we assume that an entid has at most one associated ident, and vice-versa. See + ;; http://docs.datomic.com/identity.html#idents. IDB (query-context [db] (context/->Context (source/datoms-source db) nil nil)) (schema [db] (.-schema db)) - (idents [db] (.-idents db)) + (entid [db ident] + (if (keyword? ident) + (get (.-ident-map db) ident ident) + ident)) + + (ident [db eid] + (if-not (keyword? eid) + (get (.-ident-map db) eid eid) + eid)) (current-tx [db] @@ -231,7 +246,7 @@ ;; TODO: handle exclusion across transactions here. (update db :current-tx inc)))) - (SQLite (get-in ds/value-type-map [:db.type/keyword :->SQLite]) ;; TODO: make this a protocol. exec (partial s/execute! (:sqlite-connection db))] @@ -239,9 +254,12 @@ (doseq [[ident entid] added-idents] (SQLite ident) entid])))) - db)) - (SQLite (get-in ds/value-type-map [:db.type/keyword :->SQLite]) ;; TODO: make this a protocol. exec (partial s/execute! (:sqlite-connection db))] @@ -250,7 +268,12 @@ (doseq [[attr value] attr-map] (SQLite ident) (->SQLite attr) (->SQLite value)]))))) - db)) + + (let [symbolic-schema (merge-with merge (:symbolic-schema db) fragment) + schema (ds/schema (into {} (map (fn [[k v]] [(entid db k) v]) symbolic-schema)))] + (assoc db + :symbolic-schema symbolic-schema + :schema schema)))) (close-db [db] (s/close (.-sqlite-connection db))) @@ -261,6 +284,23 @@ :cljs (.getTime (js/Date.))))) +(defn with-ident [db ident entid] + (update db :ident-map #(assoc % ident entid, entid ident))) + +(defn db [sqlite-connection idents schema current-tx] + {:pre [(map? idents) + (every? keyword? (keys idents)) + (map? schema) + (every? keyword? (keys schema))]} + (let [entid-schema (ds/schema (into {} (map (fn [[k v]] [(k idents) v]) schema))) ;; TODO: fail if ident missing. + ident-map (into idents (clojure.set/map-invert idents))] + (map->DB + {:sqlite-connection sqlite-connection + :ident-map ident-map + :symbolic-schema schema + :schema entid-schema + :current-tx current-tx}))) + ;; TODO: factor this into the overall design. (defn (db/map->DB - {:sqlite-connection sqlite-connection - :idents bootstrap/idents - :symbolic-schema bootstrap/symbolic-schema - :schema (ds/schema (into {} (map (fn [[k v]] [(k bootstrap/idents) v]) bootstrap/symbolic-schema))) ;; TODO: fail if ident missing. - :current-tx current-tx}) + (-> (db/db sqlite-connection bootstrap/idents bootstrap/symbolic-schema current-tx) ;; We use DB - {:sqlite-connection sqlite-connection - :idents idents - :symbolic-schema symbolic-schema - :schema (ds/schema (into {} (map (fn [[k v]] [(k idents) v]) symbolic-schema))) ;; TODO: fail if ident missing. - :current-tx (inc current-tx)}))))) + (db/db sqlite-connection idents symbolic-schema (inc current-tx)))))) diff --git a/src/datomish/schema_changes.cljc b/src/datomish/schema_changes.cljc index e2857cd1..c8208548 100644 --- a/src/datomish/schema_changes.cljc +++ b/src/datomish/schema_changes.cljc @@ -6,9 +6,6 @@ (:require [datomish.util :as util #?(:cljs :refer-macros :clj :refer) [raise raise-str cond-let]])) -(defn- is-install? [db [_ a & _]] - (= a (get-in db [:idents :db.install/attribute]))) - (defn datoms->schema-fragment "Turn [[:db.part/db :db.install/attribute e] [e :db/ident :attr]] into {:attr {:db/* v}}. diff --git a/src/datomish/transact.cljc b/src/datomish/transact.cljc index 683173b2..54b3ab26 100644 --- a/src/datomish/transact.cljc +++ b/src/datomish/transact.cljc @@ -106,11 +106,11 @@ entity)) (defn maybe-ident->entid [db [op e a v tx :as orig]] - (let [e (get (db/idents db) e e) ;; TODO: use ident, entid here. - a (get (db/idents db) a a) + (let [e (db/entid db e) + a (db/entid db a) v (if (ds/kw? (db/schema db) a) ;; TODO: decide if this is best. We could also check for ref and numeric types. v - (get (db/idents db) v v))] + (db/entid db v))] [op e a v tx])) (defrecord Transaction [db tempids entities]) @@ -120,7 +120,7 @@ (let [tx (:tx report) txInstant (:txInstant report)] ;; n.b., this must not be symbolic -- it's inserted after we map idents -> entids. - [:db/add tx (get-in db [:idents :db/txInstant]) txInstant])) + [:db/add tx (db/entid db :db/txInstant) txInstant])) (defn ensure-entity-form [[op e a v & rest :as entity]] (cond @@ -153,8 +153,8 @@ (defn- tx-instant? [db [op e a & _]] (and (= op :db/add) - (= e (get-in db [:idents :db/tx])) - (= a (get-in db [:idents :db/txInstant])))) + (= (db/entid db e) (db/entid db :db/tx)) + (= (db/entid db a) (db/entid db :db/txInstant)))) (defn- update-txInstant [db report] "Extract [:db/add :db/tx :db/txInstant ...], and update :txInstant with that value." @@ -175,7 +175,7 @@ ;; to unify all id-literals in :db.part/tx to the current transaction value, but also seems ;; inconsistent. tx (:tx report) - db* (assoc-in db [:idents :db/tx] tx)] + db* (db/with-ident db :db/tx tx)] (when-not (sequential? initial-es) (raise "Bad transaction data " initial-es ", expected sequential collection" {:error :transact/syntax, :tx-data initial-es})) @@ -453,7 +453,7 @@ ;; Upsert or allocate id-literals. (defn- is-ident? [db [_ a & _]] - (= a (get-in db [:idents :db/ident]))) + (= a (db/entid db :db/ident))) (defn collect-db-ident-assertions "Transactions may add idents, install new partitions, and install new schema attributes. @@ -486,15 +486,13 @@ {:error :schema/idents :op ia })))))))) -(defn- symbolicate-datom [db [e a v added]] - (let [entids (zipmap (vals (db/idents db)) (keys (db/idents db))) - symbolicate (fn [x] - (get entids x x))] - (datom - (symbolicate e) - (symbolicate a) - (symbolicate v) - added))) +(defn- symbolicate-datom [db [e a v tx added]] + (datom + (db/ident db e) + (db/ident db a) + (db/ident db v) + tx + added)) (defn collect-db-install-assertions "Transactions may add idents, install new partitions, and install new schema attributes. @@ -534,26 +532,18 @@ (collect-db-ident-assertions db) (collect-db-install-assertions db)) - idents (merge-with merge-ident (:idents db) (:added-idents report)) - symbolic-schema (merge-with merge-attr (:symbolic-schema db) (:added-attributes report)) - schema (ds/schema (into {} (map (fn [[k v]] [(k idents) v]) symbolic-schema))) db-after (-> db (db/ report diff --git a/src/datomish/transact/explode.cljc b/src/datomish/transact/explode.cljc index 232ea003..1360fac4 100644 --- a/src/datomish/transact/explode.cljc +++ b/src/datomish/transact/explode.cljc @@ -34,14 +34,14 @@ (declare explode-entity) (defn- explode-entity-a-v [db entity eid a v] - ;; a should be symbolic at this point. Map it. TODO: use ident/entid to ensure we have a symbolic attr. - (let [reverse? (reverse-ref? a) + (let [a (db/ident db a) ;; We expect a to be an ident, but it's legal to provide an entid. + a* (db/entid db a) + reverse? (reverse-ref? a) straight-a (if reverse? (reverse-ref a) a) - straight-a* (get-in db [:idents straight-a] straight-a) + straight-a* (db/entid db straight-a) _ (when (and reverse? (not (ds/ref? (db/schema db) straight-a*))) (raise "Bad attribute " a ": reverse attribute name requires {:db/valueType :db.type/ref} in schema" - {:error :transact/syntax, :attribute a, :op entity})) - a* (get-in db [:idents a] a)] + {:error :transact/syntax, :attribute a, :op entity}))] (cond reverse? (explode-entity-a-v db entity v straight-a eid) diff --git a/test/datomish/api.cljc b/test/datomish/api.cljc index 2b9bfa1c..5669a2fa 100644 --- a/test/datomish/api.cljc +++ b/test/datomish/api.cljc @@ -33,3 +33,7 @@ (def id-literal db/id-literal) (def db transact/db) + +(def entid db/entid) + +(def ident db/ident) diff --git a/test/datomish/db_test.cljc b/test/datomish/db_test.cljc index b32d2df3..8982298c 100644 --- a/test/datomish/db_test.cljc +++ b/test/datomish/db_test.cljc @@ -14,7 +14,6 @@ [datomish.sqlite :as s] [datomish.sqlite-schema] [datomish.datom] - [datomish.db :as db] #?@(:clj [[datomish.pair-chan :refer [go-pair > - (s/all-rows (:sqlite-connection db) ["SELECT e, a, v, tx FROM datoms WHERE tx > ?" tx]) - (> + (s/all-rows (:sqlite-connection db) ["SELECT e, a, v, tx FROM datoms WHERE tx > ?" tx]) + (> - (s/all-rows (:sqlite-connection db) ["SELECT a, v FROM datoms WHERE e = ?" eid]) - (> + (s/all-rows (:sqlite-connection db) ["SELECT a, v FROM datoms WHERE e = ?" eid]) + (> - (s/all-rows (:sqlite-connection db) ["SELECT e, a, v, tx, added FROM transactions WHERE tx > ? ORDER BY tx ASC, e, a, v, added" tx]) - (> + (s/all-rows (:sqlite-connection db) ["SELECT e, a, v, tx, added FROM transactions WHERE tx > ? ORDER BY tx ASC, e, a, v, added" tx]) + (