From c398c4b1537ac1907d73e4a191d6ae4398fa54e6 Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Tue, 12 Jul 2016 15:37:26 -0700 Subject: [PATCH] Part 1: Implement database on top of SQLite connection. 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. --- src/datomish/db.cljc | 38 ++++++++++++++++++++ src/datomish/sqlite.cljc | 8 +++++ src/datomish/sqlite_schema.cljc | 61 +++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 src/datomish/db.cljc create mode 100644 src/datomish/sqlite_schema.cljc diff --git a/src/datomish/db.cljc b/src/datomish/db.cljc new file mode 100644 index 00000000..b4dcc4ac --- /dev/null +++ b/src/datomish/db.cljc @@ -0,0 +1,38 @@ +;; 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 + (:require + [datomish.sqlite :as s] + [datomish.sqlite-schema :as sqlite-schema] + [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)))) + +(declare ensure-current-version) + +(defn with-sqlite-connection [sqlite-connection] + (go-pair + (when-not (= sqlite-schema/current-version (DB [sqlite-connection]))) diff --git a/src/datomish/sqlite.cljc b/src/datomish/sqlite.cljc index fb0e866f..12ac2b46 100644 --- a/src/datomish/sqlite.cljc +++ b/src/datomish/sqlite.cljc @@ -68,3 +68,11 @@ [nil e]))) (catch #?(:clj Exception :cljs js/Error) e [nil e])))) + +(defn get-user-version [db] + (go-pair + (let [row (first (!]]) + :cljs + (:require + [datomish.sqlite :as s] + [datomish.pair-chan] + [cljs.core.async :as a :refer [!]]))) + +(def current-version 1) + +(def v1-statements + ["CREATE TABLE datoms (e INTEGER NOT NULL, a TINYINT 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 TABLE transactions (e INTEGER NOT NULL, a TINYINT NOT NULL, v BLOB NOT NULL, tx INTEGER NOT NULL, added TINYINT NOT NULL DEFAULT 1)" + "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 INDEX tx ON transactions (tx)"]) + +(defn create-current-version + [db] + (go-pair + (doseq [statement v1-statements] + ( 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 + (throw (Exception. "No possible migrations!")) ;; TODO: raise. + (