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.
This commit is contained in:
parent
636b7a7957
commit
900e77862e
4 changed files with 101 additions and 1 deletions
32
src/datomish/db.cljc
Normal file
32
src/datomish/db.cljc
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
(ns datomish.db
|
||||||
|
#?(:cljs
|
||||||
|
(:require-macros
|
||||||
|
[datomish.pair-chan :refer [go-pair <?]]
|
||||||
|
[cljs.core.async.macros :refer [go]]))
|
||||||
|
(:require
|
||||||
|
[datomish.util :as util :refer [raise]]
|
||||||
|
[datomish.sqlite :as s]
|
||||||
|
[datomish.sqlite-schema :as sqlite-schema]
|
||||||
|
#?@(:clj [[datomish.pair-chan :refer [go-pair <?]]
|
||||||
|
[clojure.core.async :as a :refer [go <! >!]]])
|
||||||
|
#?@(:cljs [[datomish.pair-chan]
|
||||||
|
[cljs.core.async :as a :refer [<! >!]]])))
|
||||||
|
|
||||||
|
(defprotocol IDB
|
||||||
|
(close
|
||||||
|
[db]
|
||||||
|
"Close this database. Returns a pair channel of [nil error]."))
|
||||||
|
|
||||||
|
(defrecord DB [sqlite-connection]
|
||||||
|
IDB
|
||||||
|
(close [db] (close (.-sqlite-connection db))))
|
||||||
|
|
||||||
|
(defn <with-sqlite-connection [sqlite-connection]
|
||||||
|
(go-pair
|
||||||
|
(when-not (= sqlite-schema/current-version (<? (sqlite-schema/<ensure-current-version sqlite-connection)))
|
||||||
|
(raise "Could not ensure current SQLite schema version."))
|
||||||
|
(->DB sqlite-connection)))
|
|
@ -68,3 +68,11 @@
|
||||||
[nil e])))
|
[nil e])))
|
||||||
(catch #?(:clj Exception :cljs js/Error) e
|
(catch #?(:clj Exception :cljs js/Error) e
|
||||||
[nil e]))))
|
[nil e]))))
|
||||||
|
|
||||||
|
(defn get-user-version [db]
|
||||||
|
(go-pair
|
||||||
|
(let [row (first (<? (all-rows db ["PRAGMA user_version"])))]
|
||||||
|
(:user_version row))))
|
||||||
|
|
||||||
|
(defn set-user-version [db version]
|
||||||
|
(execute! db [(str "PRAGMA user_version = " version)]))
|
||||||
|
|
61
src/datomish/sqlite_schema.cljc
Normal file
61
src/datomish/sqlite_schema.cljc
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
(ns datomish.sqlite-schema
|
||||||
|
#?(:cljs
|
||||||
|
(:require-macros
|
||||||
|
[datomish.pair-chan :refer [go-pair <?]]
|
||||||
|
[cljs.core.async.macros :refer [go]]))
|
||||||
|
(:require
|
||||||
|
[datomish.util :as util :refer [raise]]
|
||||||
|
[datomish.sqlite :as s]
|
||||||
|
#?@(:clj [[datomish.pair-chan :refer [go-pair <?]]
|
||||||
|
[clojure.core.async :refer [go <! >!]]])
|
||||||
|
#?@(:cljs [[datomish.pair-chan]
|
||||||
|
[cljs.core.async :as a :refer [<! >!]]])))
|
||||||
|
|
||||||
|
(def current-version 1)
|
||||||
|
|
||||||
|
(def v1-statements
|
||||||
|
["CREATE TABLE datoms (e INTEGER NOT NULL, a SMALLINT NOT NULL, v BLOB NOT NULL, tx INTEGER NOT NULL, index_avet TINYINT NOT NULL DEFAULT 0, index_vaet TINYINT NOT NULL DEFAULT 0)"
|
||||||
|
"CREATE INDEX eavt ON datoms (e, a)" ;; No v -- that's an opt-in index.
|
||||||
|
"CREATE INDEX aevt ON datoms (a, e)" ;; No v -- that's an opt-in index.
|
||||||
|
"CREATE INDEX avet ON datoms (a, v, e) WHERE index_avet = 1" ;; Opt-in index: only if a has :db/index true.
|
||||||
|
"CREATE INDEX vaet ON datoms (v, a, e) WHERE index_vaet = 1" ;; Opt-in index: only if a has :db/valueType :db.type/ref
|
||||||
|
"CREATE TABLE transactions (e INTEGER NOT NULL, a SMALLINT NOT NULL, v BLOB NOT NULL, tx INTEGER NOT NULL, added TINYINT NOT NULL DEFAULT 1)"
|
||||||
|
"CREATE INDEX tx ON transactions (tx)"
|
||||||
|
"CREATE TABLE attributes (name TEXT NOT NULL PRIMARY KEY, a INTEGER UNIQUE NOT NULL)"])
|
||||||
|
|
||||||
|
(defn <create-current-version
|
||||||
|
[db]
|
||||||
|
(->>
|
||||||
|
#(go-pair
|
||||||
|
(doseq [statement v1-statements]
|
||||||
|
(<? (s/execute! db [statement])))
|
||||||
|
(<? (s/set-user-version db current-version))
|
||||||
|
(<? (s/get-user-version db)))
|
||||||
|
(s/in-transaction! db)))
|
||||||
|
|
||||||
|
(defn <update-from-version
|
||||||
|
[db from-version]
|
||||||
|
{:pre [(> from-version 0)]} ;; Or we'd create-current-version instead.
|
||||||
|
{:pre [(< from-version current-version)]} ;; Or we wouldn't need to update-from-version.
|
||||||
|
(go-pair
|
||||||
|
(raise "No migrations yet defioned!")
|
||||||
|
(<? (s/set-user-version db current-version))
|
||||||
|
(<? (s/get-user-version db))))
|
||||||
|
|
||||||
|
(defn <ensure-current-version
|
||||||
|
[db]
|
||||||
|
(go-pair
|
||||||
|
(let [v (<? (s/get-user-version db))]
|
||||||
|
(cond
|
||||||
|
(= v current-version)
|
||||||
|
v
|
||||||
|
|
||||||
|
(= v 0)
|
||||||
|
(<? (<create-current-version db))
|
||||||
|
|
||||||
|
(< v current-version)
|
||||||
|
(<? (<update-from-version db v))))))
|
|
@ -6,7 +6,6 @@
|
||||||
datomish.test.util
|
datomish.test.util
|
||||||
datomish.test.transforms
|
datomish.test.transforms
|
||||||
datomish.test.query
|
datomish.test.query
|
||||||
datomish.test.core
|
|
||||||
datomish.test-macros-test))
|
datomish.test-macros-test))
|
||||||
|
|
||||||
(doo-tests
|
(doo-tests
|
||||||
|
|
Loading…
Reference in a new issue