From ae0dac2817f2cf2607e36b308b5f1ae785621272 Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Wed, 20 Jul 2016 14:55:22 -0700 Subject: [PATCH] Propagate external scalar bindings consumed from the argument list when generating SQL. --- src/datomish/clauses.cljc | 6 +++- src/datomish/query.cljc | 69 +++++++++++++++++++++++++++++---------- 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/src/datomish/clauses.cljc b/src/datomish/clauses.cljc index 25ca43c8..bd714e41 100644 --- a/src/datomish/clauses.cljc +++ b/src/datomish/clauses.cljc @@ -190,7 +190,11 @@ ;; reorder your query yourself. (util/conj-in cc [:wheres] (not-join->where-fragment - (Not->NotJoinClause (:source cc) (:bindings cc) not)))) + (Not->NotJoinClause (:source cc) + (merge-with concat + (:external-bindings cc) + (:bindings cc)) + not)))) ;; We're keeping this simple for now: a straightforward type switch. (defn apply-clause [cc it] diff --git a/src/datomish/query.cljc b/src/datomish/query.cljc index 23adcbca..95322006 100644 --- a/src/datomish/query.cljc +++ b/src/datomish/query.cljc @@ -10,12 +10,30 @@ [datomish.projection :as projection] [datomish.transforms :as transforms] [datascript.parser :as dp - #?@(:cljs [:refer [Pattern DefaultSrc Variable Constant Placeholder]])] + #?@(:cljs + [:refer [ + BindScalar + Constant + DefaultSrc + Pattern + Placeholder + SrcVar + Variable + ]])] [clojure.string :as str] [honeysql.core :as sql] ) - #?(:clj (:import [datascript.parser Pattern DefaultSrc Variable Constant Placeholder])) - ) + #?(:clj + (:import + [datascript.parser + BindScalar + Constant + DefaultSrc + Pattern + Placeholder + SrcVar + Variable + ]))) ;; Setting this to something else will make your output more readable, ;; but not automatically safe for use. @@ -42,20 +60,35 @@ (raise-str "`with` not supported."))) (defn- validate-in [in] - (when-not (and (== 1 (count in)) - (= "$" (name (-> in first :variable :symbol)))) - (raise-str "Complex `in` not supported: " in))) + (when-not (= "$" (name (-> in first :variable :symbol))) + (raise-str "Non-default sources not supported.")) + (when-not (every? (partial instance? BindScalar) (rest in)) + (raise-str "Non-scalar bindings not supported."))) + +(defn in->bindings + "Take an `:in` list and return a bindings map suitable for use + as external bindings in a CC." + [in] + (reduce + (fn [m b] + (or + (when (instance? BindScalar b) + (let [var (:variable b)] + (when (instance? Variable var) + (let [v (:symbol var)] + (assoc m v [(sql/param (util/var->sql-var v))]))))) + m)) + {} + in)) (defn expand-find-into-context [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. (let [{:keys [find in with where]} find] ; Destructure the Datalog query. (validate-with with) (validate-in in) - (assoc context - :elements (:elements find) - :cc (clauses/patterns->cc (:default-source context) where nil)))) + (let [external-bindings (in->bindings in)] + (assoc context + :elements (:elements find) + :cc (clauses/patterns->cc (:default-source context) where external-bindings))))) (defn find->sql-clause "Take a parsed `find` expression and turn it into a structured SQL @@ -70,10 +103,10 @@ (defn find->sql-string "Take a parsed `find` expression and turn it into SQL." - [context find] + [context find args] (-> (find->sql-clause context find) - (sql/format :quoting sql-quoting-style))) + (sql/format args :quoting sql-quoting-style))) (defn parse "Parse a Datalog query array into a structured `find` expression." @@ -82,10 +115,12 @@ (comment (def sql-quoting-style nil) - (datomish.query/find->sql-string (datomish.context/->Context (datomish.source/datoms-source nil) nil nil) + (datomish.query/find->sql-string + (datomish.context/->Context (datomish.source/datoms-source nil) nil nil) (datomish.query/parse - '[:find ?timestampMicros ?page :in $ :where + '[:find ?timestampMicros ?page :in $ ?latest :where [?page :page/starred true ?t] [?t :db/txInstant ?timestampMicros] - (not [(> ?t 1000000)]) ])) + (not [(> ?t ?latest)]) ]) + {:latest 5}) )