diff --git a/CHANGELOG.md b/CHANGELOG.md index 50d19c0..97970cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * 2.2.next in progress * Address #377 by adding `honey.sql/map=` to convert a hash map into an equality condition (for a `WHERE` clause). + * Address #351 by adding a `:cache` option to `honey.sql/format` (for Clojure only, not ClojureScript). * Address #281 by adding support for `SELECT * EXCEPT ..` and `SELECT * REPLACE ..` and `ARRAY<>` and `STRUCT<>` column types -- see [SQL Clause Reference - SELECT](https://cljdoc.org/d/com.github.seancorfield/honeysql/CURRENT/doc/getting-started/sql-clause-reference#select-select-distinct) and [SQL Clause Reference - DDL](https://cljdoc.org/d/com.github.seancorfield/honeysql/CURRENT/doc/getting-started/sql-clause-reference#ddl-clauses) respectively for more details. * Update `build-clj` to v0.6.7. diff --git a/deps.edn b/deps.edn index b1e7643..b746dfb 100644 --- a/deps.edn +++ b/deps.edn @@ -16,7 +16,8 @@ :test {:extra-paths ["test"] :extra-deps {io.github.cognitect-labs/test-runner - {:git/tag "v0.5.0" :git/sha "48c3c67"}} + {:git/tag "v0.5.0" :git/sha "48c3c67"} + org.clojure/core.cache {:mvn/version "RELEASE"}} :exec-fn cognitect.test-runner.api/test} ;; various "runners" for tests/CI: diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index b33d4c5..582d40e 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -1,4 +1,4 @@ -;; copyright (c) 2020-2021 sean corfield, all rights reserved +;; copyright (c) 2020-2022 sean corfield, all rights reserved (ns honey.sql "Primary API for HoneySQL 2.x. @@ -1374,7 +1374,8 @@ as named arguments followed by other options in a hash map." ([data] (format data {})) ([data opts] - (let [dialect? (contains? opts :dialect) + (let [cache (:cache opts) + dialect? (contains? opts :dialect) dialect (when dialect? (get dialects (check-dialect (:dialect opts))))] (binding [*dialect* (if dialect? dialect @default-dialect) *checking* (if (contains? opts :checking) @@ -1397,7 +1398,16 @@ (:quoted-snake opts)) *params* (:params opts) *values-default-columns* (:values-default-columns opts)] - (mapv #(unwrap % opts) (format-dsl data opts))))) + (if cache + #?(:clj + ;; prefer requiring-resolve but that's 1.10+ only: + (let [_ (require 'clojure.core.cache.wrapped) + through (resolve 'clojure.core.cache.wrapped/lookup-or-miss)] + (->> (through cache data (fn [_] (format-dsl data (dissoc opts :cache)))) + (mapv #(unwrap % opts)))) + :cljs + (throw (ex-info "cached queries are not supported in ClojureScript" opts))) + (mapv #(unwrap % opts) (format-dsl data opts)))))) ([data k v & {:as opts}] (format data (assoc opts k v)))) (defn set-dialect! diff --git a/test/honey/cache_test.clj b/test/honey/cache_test.clj new file mode 100644 index 0000000..bf05fab --- /dev/null +++ b/test/honey/cache_test.clj @@ -0,0 +1,68 @@ +;; copyright (c) 2022 sean corfield, all rights reserved + +(ns honey.cache-test + (:refer-clojure :exclude [format group-by]) + (:require [clojure.core.cache.wrapped :as cache] + [clojure.test :refer [deftest is]] + [honey.sql :as sut] + [honey.sql.helpers + :refer [select-distinct from join left-join right-join where + group-by having order-by limit offset]])) + +(def big-complicated-map + (-> (select-distinct :f.* :b.baz :c.quux [:b.bla "bla-bla"] + [[:now]] [[:raw "@x := 10"]]) + (from [:foo :f] [:baz :b]) + (join :draq [:= :f.b :draq.x] + :eldr [:= :f.e :eldr.t]) + (left-join [:clod :c] [:= :f.a :c.d]) + (right-join :bock [:= :bock.z :c.e]) + (where [:or + [:and [:= :f.a "bort"] [:not= :b.baz [:param :param1]]] + [:and [:< 1 2] [:< 2 3]] + [:in :f.e [1 [:param :param2] 3]] + [:between :f.e 10 20]]) + (group-by :f.a :c.e) + (having [:< 0 :f.e]) + (order-by [:b.baz :desc] :c.quux [:f.a :nulls-first]) + (limit 50) + (offset 10))) + +(deftest cache-tests + (let [cache (cache/basic-cache-factory {})] + (is (= ["SELECT * FROM table WHERE id = ?" 1] + (sut/format {:select [:*] :from [:table] :where [:= :id 1]} + {:cache cache}) + (sut/format {:select [:*] :from [:table] :where [:= :id 1]} + {:cache cache}))) + (is (= (sut/format {:select [:*] :from [:table] :where [:= :id 1]}) + (sut/format {:select [:*] :from [:table] :where [:= :id 1]} + {:cache cache}))) + (is (= (sut/format big-complicated-map {:params {:param1 "gabba" :param2 2}}) + (sut/format big-complicated-map {:cache cache :params {:param1 "gabba" :param2 2}}) + (sut/format big-complicated-map {:cache cache :params {:param1 "gabba" :param2 2}}))) + (is (= (sut/format big-complicated-map {:params {:param1 "foo" :param2 42}}) + (sut/format big-complicated-map {:cache cache :params {:param1 "foo" :param2 42}}) + (sut/format big-complicated-map {:cache cache :params {:param1 "foo" :param2 42}}))) + (println "Uncached, simple, embedded") + (time (dotimes [_ 100000] + (sut/format {:select [:*] :from [:table] :where [:= :id 1]}))) + (println "Cached, simple, embedded") + (time (dotimes [_ 100000] + (sut/format {:select [:*] :from [:table] :where [:= :id 1]} {:cache cache}))) + (println "Uncached, complex, mixed") + (time (dotimes [_ 10000] + (sut/format big-complicated-map {:params {:param1 "gabba" :param2 2}}))) + (println "Cached, complex, mixed") + (time (dotimes [_ 10000] + (sut/format big-complicated-map {:cache cache :params {:param1 "gabba" :param2 2}})))) + (let [cache (cache/basic-cache-factory {})] + (is (= ["SELECT * FROM table WHERE id = ?" 1] + (sut/format {:select [:*] :from [:table] :where [:= :id :?id]} + {:cache cache :params {:id 1}}) + (sut/format {:select [:*] :from [:table] :where [:= :id :?id]} + {:cache cache :params {:id 1}}))) + (is (= (sut/format {:select [:*] :from [:table] :where [:= :id :?id]} + {:params {:id 2}}) + (sut/format {:select [:*] :from [:table] :where [:= :id :?id]} + {:cache cache :params {:id 2}}))))) diff --git a/test/honey/sql_test.cljc b/test/honey/sql_test.cljc index 7dbc51b..652e8b1 100644 --- a/test/honey/sql_test.cljc +++ b/test/honey/sql_test.cljc @@ -1,4 +1,4 @@ -;; copyright (c) 2021 sean corfield, all rights reserved +;; copyright (c) 2021-2022 sean corfield, all rights reserved (ns honey.sql-test (:refer-clojure :exclude [format])