diff --git a/src/datomish/clauses.cljc b/src/datomish/clauses.cljc index bd714e41..7d6b69b8 100644 --- a/src/datomish/clauses.cljc +++ b/src/datomish/clauses.cljc @@ -32,7 +32,8 @@ ;; * Projection expressions, if only used for output. ;; * Inline expressions? ;; `not` turns into NOT EXISTS with WHERE clauses inside the subquery to -;; bind it to the outer variables. +;; bind it to the outer variables, or adds simple WHERE clauses to the outer +;; clause. ;; `not-join` is similar, but with explicit binding. ;; `or` turns into a collection of UNIONs inside a subquery. ;; `or`'s documentation states that all clauses must include the same vars, diff --git a/src/datomish/db.cljc b/src/datomish/db.cljc index b7b709a8..6f2af777 100644 --- a/src/datomish/db.cljc +++ b/src/datomish/db.cljc @@ -8,25 +8,63 @@ [datomish.pair-chan :refer [go-pair !]]]) + [clojure.core.async :as a :refer [chan go !]]]) #?@(:cljs [[datomish.pair-chan] - [cljs.core.async :as a :refer [!]]]))) + [cljs.core.async :as a :refer [chan !]]]))) (defprotocol IDB + (query-context + [db]) (close [db] "Close this database. Returns a pair channel of [nil error].")) (defrecord DB [sqlite-connection] IDB - (close [db] (close (.-sqlite-connection db)))) + (query-context [db] (context/->Context (source/datoms-source db) nil nil)) + (close [db] (s/close (.-sqlite-connection db)))) (defn DB sqlite-connection))) + +(defn db + query-context + (query/expand-find-into-context parsed)) + row-pair-transducer (projection/row-pair-transducer context) + sql (query/context->sql-string context args) + chan (chan 50 row-pair-transducer)] + + (s/! chan close! take!]]]) - #?@(:cljs - [[datomish.promise-sqlite] - [datomish.pair-chan] - [datomish.util] - [cljs.core.async :as a :refer - [! chan close! take!]]]))) - -(defn sql-string context) chan) - chan)) diff --git a/src/datomish/exec_repl.cljc b/src/datomish/exec_repl.cljc index 575430bc..369ef54b 100644 --- a/src/datomish/exec_repl.cljc +++ b/src/datomish/exec_repl.cljc @@ -9,6 +9,7 @@ [datomish.pair-chan :refer [go-pair lazy-seq (exec/ cc :bindings variable first) (raise-str "Couldn't find variable " variable))) @@ -36,6 +35,7 @@ @param context A Context, containing elements. @return a sequence of pairs." [context] + (def foo context) (let [elements (:elements context)] (when-not (every? #(instance? Variable %1) elements) (raise-str "Unable to :find non-variables.")) @@ -44,10 +44,10 @@ [(lookup-variable (:cc context) var) (util/var->sql-var var)])) elements))) -(defn row-pair-transducer [context projection] +(defn row-pair-transducer [context] ;; For now, we only support straight var lists, so ;; our transducer is trivial. - (let [columns-in-order (map second projection)] + (let [columns-in-order (map second (sql-projection context))] (map (fn [[row err]] (if err [row err] diff --git a/src/datomish/query.cljc b/src/datomish/query.cljc index 95322006..bc59b2e1 100644 --- a/src/datomish/query.cljc +++ b/src/datomish/query.cljc @@ -60,6 +60,8 @@ (raise-str "`with` not supported."))) (defn- validate-in [in] + (when (nil? in) + (raise-str ":in expression cannot be nil.")) (when-not (= "$" (name (-> in first :variable :symbol))) (raise-str "Non-default sources not supported.")) (when-not (every? (partial instance? BindScalar) (rest in)) @@ -81,7 +83,11 @@ {} in)) -(defn expand-find-into-context [context find] +(defn find-into-context + "Take a parsed `find` expression and return a fully populated + Context. You'll want this so you can get access to the + projection, amongst other things." + [context find] (let [{:keys [find in with where]} find] ; Destructure the Datalog query. (validate-with with) (validate-in in) @@ -90,15 +96,18 @@ :elements (:elements find) :cc (clauses/patterns->cc (:default-source context) where external-bindings))))) +(defn context->sql-string + [context args] + (-> context + context->sql-clause + (sql/format args :quoting sql-quoting-style))) + (defn find->sql-clause "Take a parsed `find` expression and turn it into a structured SQL expression that can be formatted by honeysql." [context find] - ;; There's some confusing use of 'where' and friends here. That's because - ;; the parsed Datalog includes :where, and it's also input to honeysql's - ;; SQL formatter. (->> find - (expand-find-into-context context) + (find-into-context context) context->sql-clause)) (defn find->sql-string diff --git a/src/datomish/sqlite.cljc b/src/datomish/sqlite.cljc index db4e804a..299b64ee 100644 --- a/src/datomish/sqlite.cljc +++ b/src/datomish/sqlite.cljc @@ -59,7 +59,9 @@ when no more results exist. Consume with