Propagate external scalar bindings consumed from the argument list when generating SQL.

This commit is contained in:
Richard Newman 2016-07-20 14:55:22 -07:00
parent fbd8c0bfbb
commit ae0dac2817
2 changed files with 57 additions and 18 deletions

View file

@ -190,7 +190,11 @@
;; reorder your query yourself. ;; reorder your query yourself.
(util/conj-in cc [:wheres] (util/conj-in cc [:wheres]
(not-join->where-fragment (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. ;; We're keeping this simple for now: a straightforward type switch.
(defn apply-clause [cc it] (defn apply-clause [cc it]

View file

@ -10,12 +10,30 @@
[datomish.projection :as projection] [datomish.projection :as projection]
[datomish.transforms :as transforms] [datomish.transforms :as transforms]
[datascript.parser :as dp [datascript.parser :as dp
#?@(:cljs [:refer [Pattern DefaultSrc Variable Constant Placeholder]])] #?@(:cljs
[:refer [
BindScalar
Constant
DefaultSrc
Pattern
Placeholder
SrcVar
Variable
]])]
[clojure.string :as str] [clojure.string :as str]
[honeysql.core :as sql] [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, ;; Setting this to something else will make your output more readable,
;; but not automatically safe for use. ;; but not automatically safe for use.
@ -42,20 +60,35 @@
(raise-str "`with` not supported."))) (raise-str "`with` not supported.")))
(defn- validate-in [in] (defn- validate-in [in]
(when-not (and (== 1 (count in)) (when-not (= "$" (name (-> in first :variable :symbol)))
(= "$" (name (-> in first :variable :symbol)))) (raise-str "Non-default sources not supported."))
(raise-str "Complex `in` not supported: " in))) (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] (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. (let [{:keys [find in with where]} find] ; Destructure the Datalog query.
(validate-with with) (validate-with with)
(validate-in in) (validate-in in)
(let [external-bindings (in->bindings in)]
(assoc context (assoc context
:elements (:elements find) :elements (:elements find)
:cc (clauses/patterns->cc (:default-source context) where nil)))) :cc (clauses/patterns->cc (:default-source context) where external-bindings)))))
(defn find->sql-clause (defn find->sql-clause
"Take a parsed `find` expression and turn it into a structured SQL "Take a parsed `find` expression and turn it into a structured SQL
@ -70,10 +103,10 @@
(defn find->sql-string (defn find->sql-string
"Take a parsed `find` expression and turn it into SQL." "Take a parsed `find` expression and turn it into SQL."
[context find] [context find args]
(-> (->
(find->sql-clause context find) (find->sql-clause context find)
(sql/format :quoting sql-quoting-style))) (sql/format args :quoting sql-quoting-style)))
(defn parse (defn parse
"Parse a Datalog query array into a structured `find` expression." "Parse a Datalog query array into a structured `find` expression."
@ -82,10 +115,12 @@
(comment (comment
(def sql-quoting-style nil) (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 (datomish.query/parse
'[:find ?timestampMicros ?page :in $ :where '[:find ?timestampMicros ?page :in $ ?latest :where
[?page :page/starred true ?t] [?page :page/starred true ?t]
[?t :db/txInstant ?timestampMicros] [?t :db/txInstant ?timestampMicros]
(not [(> ?t 1000000)]) ])) (not [(> ?t ?latest)]) ])
{:latest 5})
) )