Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exists #151

Merged
merged 11 commits into from
Mar 22, 2019
6 changes: 4 additions & 2 deletions src/walkable/sql_query_builder.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,17 @@

(defn process-supplied-condition
[{::keys [floor-plan] :as env}]
(let [{::floor-plan/keys [compiled-formulas join-filter-subqueries]}
(let [{::floor-plan/keys [compiled-formulas compiled-exists-forms
join-filter-subqueries]}
floor-plan

supplied-condition
(get-in env [:ast :params :filters])]
(when supplied-condition
(->> supplied-condition
(expressions/compile-to-string
{:join-filter-subqueries join-filter-subqueries})
{:compiled-exists-forms compiled-exists-forms
:join-filter-subqueries join-filter-subqueries})
(expressions/substitute-atomic-variables
{:variable-values compiled-formulas})))))

Expand Down
8 changes: 8 additions & 0 deletions src/walkable/sql_query_builder/expressions.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@
{:raw-string (str "CAST (? AS " type-str ")")
:params [(process-expression env expression)]})))

(defmethod unsafe-expression? :exists [_operator] true)

(defmethod process-unsafe-expression :exists
[{:keys [compiled-exists-forms] :as env} [_operator [column-keyword]]]
(if-let [compiled (get compiled-exists-forms column-keyword)]
compiled
(throw (ex-info "Invalid column supplied to :exists" {}))))

(defmethod operator? :and [_operator] true)

(defmethod process-operator :and
Expand Down
58 changes: 45 additions & 13 deletions src/walkable/sql_query_builder/floor_plan.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@
(zipmap ks
(map #(emitter/clojuric-name emitter %) ks)))

(defn compile-exists [emitter column-keyword]
{:raw-string (str "EXISTS (SELECT "
(emitter/column-name emitter column-keyword)
" FROM "
(emitter/table-name emitter column-keyword)
")")
:params []})

(defn compile-exists-forms [emitter column-keywords]
(apply hash-map
(interleave column-keywords
(map #(compile-exists emitter %) column-keywords))))

(s/def ::without-join-table
(s/coll-of ::expressions/namespaced-keyword
:count 2))
Expand Down Expand Up @@ -233,9 +246,11 @@
(defn rotate [coll]
(take (count coll) (drop 1 (cycle coll))))

(defn compile-formulas-once [compiled-true-columns formulas]
(defn compile-formulas-once [compiled-true-columns compiled-exists-forms formulas]
(reduce-kv (fn [result k expression]
(let [compiled (expressions/compile-to-string {} expression)]
(let [compiled (expressions/compile-to-string
{:compiled-exists-forms compiled-exists-forms}
expression)]
(if (unbound-expression? compiled)
(update result :unbound assoc k compiled)
(update result :bound assoc k compiled))))
Expand Down Expand Up @@ -368,9 +383,10 @@
(map #(expressions/verbatim-raw-string (emitter/column-name emitter %)) ks)))

(defn compile-formulas
[{:keys [true-columns pseudo-columns aggregators emitter] :as floor-plan}]
[{:keys [true-columns pseudo-columns aggregators emitter compiled-exists-forms]
:as floor-plan}]
(let [compiled-formulas (-> (compile-true-columns emitter true-columns)
(compile-formulas-once (merge pseudo-columns aggregators))
(compile-formulas-once compiled-exists-forms (merge pseudo-columns aggregators))
compile-formulas-recursively)
_ok? (column-dependencies compiled-formulas)
{:keys [bound unbound]} compiled-formulas
Expand All @@ -388,13 +404,15 @@
(assoc floor-plan :compiled-selection compiled-selection)))

(defn compile-ident-conditions
[{:keys [conditional-idents compiled-formulas] :as floor-plan}]
[{:keys [conditional-idents compiled-formulas compiled-exists-forms]
:as floor-plan}]
(let [compiled-ident-conditions
(reduce-kv (fn [acc k ident-key]
(assoc acc k
(expressions/substitute-atomic-variables
{:variable-values compiled-formulas}
(expressions/compile-to-string {}
(expressions/compile-to-string
{:compiled-exists-forms compiled-exists-forms}
[:= ident-key (expressions/av `ident-value)]))))
{}
conditional-idents)]
Expand Down Expand Up @@ -441,15 +459,17 @@
(assoc :compiled-aggregator-selection compiled-aggregator-selection))))

(defn compile-join-conditions
[{:keys [joins compiled-formulas target-columns] :as floor-plan}]
[{:keys [joins compiled-formulas target-columns compiled-exists-forms]
:as floor-plan}]
(let [compiled-join-conditions
(reduce-kv (fn [acc k join-seq]
(let [target-column (get target-columns k)
clojuric-name (get clojuric-names target-column)]
(assoc acc k
(expressions/substitute-atomic-variables
{:variable-values compiled-formulas}
(expressions/compile-to-string {}
(expressions/compile-to-string
{:compiled-exists-forms compiled-exists-forms}
[:= target-column (expressions/av `source-column-value)])))))
{}
joins)]
Expand Down Expand Up @@ -488,13 +508,16 @@
(dissoc :joins))))

(defn compile-extra-conditions
[{:keys [extra-conditions compiled-formulas join-filter-subqueries] :as floor-plan}]
[{:keys [extra-conditions compiled-formulas compiled-exists-forms
join-filter-subqueries]
:as floor-plan}]
(let [compiled-extra-conditions
(reduce-kv (fn [acc k extra-condition]
(assoc acc k
(->> extra-condition
(expressions/compile-to-string
{:join-filter-subqueries join-filter-subqueries})
{:compiled-exists-forms compiled-exists-forms
:join-filter-subqueries join-filter-subqueries})
(expressions/substitute-atomic-variables
{:variable-values compiled-formulas}))))
{}
Expand All @@ -509,10 +532,12 @@
:params [compiled-having]}))

(defn compile-having
[{:keys [compiled-formulas join-filter-subqueries]} having-condition]
[{:keys [compiled-formulas join-filter-subqueries compiled-exists-forms]}
having-condition]
(->> having-condition
(expressions/compile-to-string
{:join-filter-subqueries join-filter-subqueries})
{:compiled-exists-forms compiled-exists-forms
:join-filter-subqueries join-filter-subqueries})
(expressions/substitute-atomic-variables
{:variable-values compiled-formulas})
prefix-having))
Expand Down Expand Up @@ -592,6 +617,7 @@
:batch-query
:cardinality
:clojuric-names
:compiled-exists-forms
:column-keywords
:compiled-extra-conditions
:compiled-formulas
Expand Down Expand Up @@ -704,6 +730,11 @@
(-> floor-plan
(assoc :clojuric-names (clojuric-names emitter column-keywords))))

(defn prepare-exists-forms
[{:keys [emitter column-keywords] :as floor-plan}]
(-> floor-plan
(assoc :compiled-exists-forms (compile-exists-forms emitter column-keywords))))

(defn separate-floor-plan-keys
[{:keys [joins emitter idents
extra-conditions]
Expand All @@ -712,7 +743,8 @@
separate-idents
polulate-columns-with-condititional-idents
prepare-keywords
prepare-clojuric-names))
prepare-clojuric-names
prepare-exists-forms))

(defn precompile-floor-plan
[{:keys [joins emitter idents unconditional-idents conditional-idents] :as floor-plan}]
Expand Down
27 changes: 25 additions & 2 deletions test/walkable/sql_query_builder/floor_plan_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
(is (= (sut/clojuric-names emitter/mysql-emitter [:foo/bar :loo/lar])
{:foo/bar "`foo/bar`", :loo/lar "`loo/lar`"})))

(deftest compile-exists-test
(is (= (sut/compile-exists emitter/default-emitter :foo/bar)
{:raw-string "EXISTS (SELECT \"foo\".\"bar\" FROM \"foo\")", :params []})))

(deftest target-column-tests
(is (= (sut/target-column [:pet/owner :person/number])
:person/number))
Expand Down Expand Up @@ -142,6 +146,7 @@
(is (= (sut/compile-formulas-once
(sut/compile-true-columns emitter/postgres-emitter
#{:x/a :x/b})
{}
{:x/c 99
:x/d [:- 100 :x/c]})
{:unbound #:x {:d {:params [(expressions/av :x/c)],
Expand All @@ -151,24 +156,42 @@
:c {:raw-string "99", :params []}}})))

(deftest compile-formulas-recursively-test
(is (= (sut/compile-formulas-recursively
(sut/compile-formulas-once
(sut/compile-true-columns
emitter/postgres-emitter #{:x/a :x/b})
{}
{:x/c 99
:x/d [:- 100 :x/c]}))
{:unbound {},
:bound #:x {:a {:raw-string "\"x\".\"a\"", :params []},
:b {:raw-string "\"x\".\"b\"", :params []},
:c {:raw-string "99", :params []},
:d {:raw-string "(100)-(99)", :params []}}}))

(is (= (sut/compile-formulas-recursively
(sut/compile-formulas-once
(sut/compile-true-columns
emitter/postgres-emitter #{:x/a :x/b})
(sut/compile-exists-forms
emitter/postgres-emitter #{:x/a :x/b})
{:x/c 99
:x/d [:- 100 :x/c]}))
:x/d [:- 100 :x/c]
:x/e [:exists :x/a]}))
{:unbound {},
:bound #:x {:a {:raw-string "\"x\".\"a\"", :params []},
:b {:raw-string "\"x\".\"b\"", :params []},
:c {:raw-string "99", :params []},
:d {:raw-string "(100)-(99)", :params []}}})))
:d {:raw-string "(100)-(99)", :params []}
:e {:raw-string "EXISTS (SELECT \"x\".\"a\" FROM \"x\")", :params []}}})))

(deftest column-dependencies-test
(is (= (sut/column-dependencies
(sut/compile-formulas-recursively
(sut/compile-formulas-once
(sut/compile-true-columns
emitter/postgres-emitter #{:x/a :x/b})
{}
{:x/c [:+ :x/d (expressions/av 'o)]
:x/d [:- 100 :x/e]
:x/e [:- 100 :x/c]})))
Expand Down