diff --git a/project.clj b/project.clj index 12ba1dfd..7208a1ae 100644 --- a/project.clj +++ b/project.clj @@ -42,7 +42,9 @@ :profiles {:dev {:dependencies [[com.cemerick/piggieback "0.2.1"] [org.clojure/tools.nrepl "0.2.10"] - [tempfile "0.2.0"]] + [tempfile "0.2.0"] + [org.clojure/java.jdbc "0.6.2-alpha1"] + [org.xerial/sqlite-jdbc "3.8.11.2"]] :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]} :plugins [[lein-cljsbuild "1.1.2"] [lein-doo "0.1.6"]] diff --git a/src/datomish/jdbc_sqlite.clj b/src/datomish/jdbc_sqlite.clj new file mode 100644 index 00000000..421707d5 --- /dev/null +++ b/src/datomish/jdbc_sqlite.clj @@ -0,0 +1,40 @@ +;; 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.jdbc-sqlite + (:require + [datomish.pair-chan :refer [go-pair]] + [datomish.sqlite :as s] + [clojure.java.jdbc :as j] + [clojure.core.async :as a])) + +(deftype JDBCSQLiteConnection [spec] + s/ISQLiteConnection + (-execute! + [db sql bindings] + (go-pair + (j/execute! (.-spec db) (into [sql] bindings) {:transaction? false}))) + + (-each + [db sql bindings row-cb] + (go-pair + (let [rows (j/query (.-spec db) (into [sql] bindings))] + (when row-cb + (doseq [row rows] (row-cb row))) + (count rows)))) + + (close [db] + (go-pair + (.close (:connection (.-spec db)))))) + +(defn open + [path & {:keys [mode]}] + (let [spec {:classname "org.sqlite.JDBC" + :subprotocol "sqlite" + :subname path}] ;; TODO: use mode. + (go-pair + (->> + (j/get-connection spec) + (assoc spec :connection) + (->JDBCSQLiteConnection))))) diff --git a/src/datomish/promise_sqlite.cljs b/src/datomish/promise_sqlite.cljs new file mode 100644 index 00000000..1aff74a7 --- /dev/null +++ b/src/datomish/promise_sqlite.cljs @@ -0,0 +1,37 @@ +;; 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.promise-sqlite + (:require + [datomish.sqlite :as s] + [cljs-promises.async] + [cljs.nodejs :as nodejs])) + +(def sqlite (nodejs/require "promise-sqlite")) + +(defrecord SQLite3Connection [db] + s/ISQLiteConnection + (-execute! + [db sql bindings] + (cljs-promises.async/pair-port + (.run (.-db db) sql (or (clj->js bindings) #js [])))) + + (-each + [db sql bindings row-cb] + (let [cb (fn [row] + (row-cb (js->clj row :keywordize-keys true)))] + (cljs-promises.async/pair-port + (.each (.-db db) sql (or (clj->js bindings) #js []) (when row-cb cb))))) + + (close + [db] + (cljs-promises.async/pair-port + (.close (.-db db))))) + +(defn open + [path & {:keys [mode] :or {:mode 6}}] + (cljs-promises.async/pair-port + (-> + (.open sqlite.DB path (clj->js {:mode mode})) + (.then ->SQLite3Connection)))) diff --git a/src/datomish/sqlite.cljc b/src/datomish/sqlite.cljc new file mode 100644 index 00000000..fb0e866f --- /dev/null +++ b/src/datomish/sqlite.cljc @@ -0,0 +1,70 @@ +;; 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 + #?(:cljs + (:require-macros + [datomish.pair-chan :refer [go-pair !]]) + :cljs + (:require + [datomish.pair-chan] + [cljs.core.async :as a :refer [!]]))) + +(defprotocol ISQLiteConnection + (-execute! + [db sql bindings] + "Execute the given SQL string with the specified bindings. Returns a pair channel resolving + to a query dependent `[result error]` pair.") + + (-each + [db sql bindings row-cb] + "Execute the given SQL string with the specified bindings, invoking the given `row-cb` callback + function (if provided) with each returned row. Each row will be presented to `row-cb` as a + map-like object, such that `(:column-name row)` succeeds. Returns a pair channel of `[result + error]`, where `result` to the number of rows returned.") + + (close + [db] + "Close this SQLite connection. Returns a pair channel of [nil error].")) + +(defn execute! + [db [sql & bindings]] + (-execute! db sql bindings)) + +(defn each-row + [db [sql & bindings] row-cb] + (-each db sql bindings row-cb)) + +(defn reduce-rows + [db [sql & bindings] initial f] + (let [acc (atom initial)] + (go + (let [[_ err] (!]] + [clojure.test :as t :refer [is are deftest testing]])) + +(deftest-async test-all-rows + (with-tempfile [t (tempfile)] + (with-open [db (!]] + [cljs.test :refer-macros [is are deftest testing async]] + [datomish.pair-chan] + [datomish.sqlite :as s] + [datomish.promise-sqlite :as ps])) + +(deftest-async test-all-rows + (with-tempfile [t (tempfile)] + (with-open [db (