make clause-building extensible, move helper fns to honeysql.helpers
This commit is contained in:
parent
3b97fd4f06
commit
18d5fa19e8
3 changed files with 166 additions and 170 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
(:refer-clojure :exclude [group-by format])
|
(:refer-clojure :exclude [group-by format])
|
||||||
(:require [honeysql.format :as format]
|
(:require [honeysql.format :as format]
|
||||||
[honeysql.types :as types]
|
[honeysql.types :as types]
|
||||||
|
[honeysql.helpers :refer [build-clause]]
|
||||||
[honeysql.util :refer [defalias]]))
|
[honeysql.util :refer [defalias]]))
|
||||||
|
|
||||||
(defalias call types/call)
|
(defalias call types/call)
|
||||||
|
|
@ -9,157 +10,12 @@
|
||||||
(defalias format format/format)
|
(defalias format format/format)
|
||||||
(defalias format-predicate format/format-predicate)
|
(defalias format-predicate format/format-predicate)
|
||||||
|
|
||||||
(defn select [& fields]
|
(defn build [& clauses]
|
||||||
(let [[m fields] (if (map? (first fields))
|
|
||||||
[(first fields) (rest fields)]
|
|
||||||
[{} fields])]
|
|
||||||
(assoc m :select fields)))
|
|
||||||
|
|
||||||
(defn merge-select [sql-map & fields]
|
|
||||||
(update-in sql-map [:select] concat fields))
|
|
||||||
|
|
||||||
(defn un-select [sql-map & fields]
|
|
||||||
(update-in sql-map [:select] #(remove (set fields) %)))
|
|
||||||
|
|
||||||
(defn from [& tables]
|
|
||||||
(let [[m tables] (if (map? (first tables))
|
|
||||||
[(first tables) (rest tables)]
|
|
||||||
[{} tables])]
|
|
||||||
(assoc m :from tables)))
|
|
||||||
|
|
||||||
(defn merge-from [sql-map & tables]
|
|
||||||
(update-in sql-map [:from] concat tables))
|
|
||||||
|
|
||||||
(defn where [& preds]
|
|
||||||
(let [[m preds] (if (map? (first preds))
|
|
||||||
[(first preds) (rest preds)]
|
|
||||||
[{} preds])]
|
|
||||||
(assoc m :where (if (= 1 (count preds))
|
|
||||||
(first preds)
|
|
||||||
(vec (cons :and preds))))))
|
|
||||||
|
|
||||||
(defn merge-where [sql-map & preds]
|
|
||||||
(if (empty? preds)
|
|
||||||
sql-map
|
|
||||||
(let [[merge-op preds] (if (keyword? (first preds))
|
|
||||||
[(first preds) (rest preds)]
|
|
||||||
[:and preds])]
|
|
||||||
(assoc sql-map :where (if (contains? sql-map :where)
|
|
||||||
(vec (concat [merge-op (:where sql-map)] preds))
|
|
||||||
(vec (if (= 1 (count preds))
|
|
||||||
(first preds)
|
|
||||||
(cons merge-op preds))))))))
|
|
||||||
|
|
||||||
(defn join [& clauses]
|
|
||||||
(let [[m clauses] (if (map? (first clauses))
|
|
||||||
[(first clauses) (rest clauses)]
|
|
||||||
[{} clauses])]
|
|
||||||
(assoc m :join clauses)))
|
|
||||||
|
|
||||||
(defn merge-join [sql-map & clauses]
|
|
||||||
(update-in sql-map [:join] concat clauses))
|
|
||||||
|
|
||||||
(defn group-by [& fields]
|
|
||||||
(let [[m fields] (if (map? (first fields))
|
|
||||||
[(first fields) (rest fields)]
|
|
||||||
[{} fields])]
|
|
||||||
(assoc m :group-by fields)))
|
|
||||||
|
|
||||||
(defn merge-group-by [sql-map & fields]
|
|
||||||
(update-in sql-map [:group-by] concat fields))
|
|
||||||
|
|
||||||
(defn having [& preds]
|
|
||||||
(let [[m preds] (if (map? (first preds))
|
|
||||||
[(first preds) (rest preds)]
|
|
||||||
[{} preds])]
|
|
||||||
(assoc m :having (if (= 1 (count preds))
|
|
||||||
(first preds)
|
|
||||||
(vec (cons :and preds))))))
|
|
||||||
|
|
||||||
(defn merge-having [sql-map & preds]
|
|
||||||
(if (empty? preds)
|
|
||||||
sql-map
|
|
||||||
(let [[merge-op preds] (if (keyword? (first preds))
|
|
||||||
[(first preds) (rest preds)]
|
|
||||||
[:and preds])]
|
|
||||||
(assoc sql-map :having (if (contains? sql-map :having)
|
|
||||||
(vec (concat [merge-op (:having sql-map)] preds))
|
|
||||||
(vec (if (= 1 (count preds))
|
|
||||||
(first preds)
|
|
||||||
(cons merge-op preds))))))))
|
|
||||||
|
|
||||||
(defn order-by [& fields]
|
|
||||||
(let [[m fields] (if (map? (first fields))
|
|
||||||
[(first fields) (rest fields)]
|
|
||||||
[{} fields])]
|
|
||||||
(assoc m :order-by fields)))
|
|
||||||
|
|
||||||
(defn merge-order-by [sql-map & fields]
|
|
||||||
(update-in sql-map [:order-by] concat fields))
|
|
||||||
|
|
||||||
(defn limit
|
|
||||||
([l]
|
|
||||||
(limit {} l))
|
|
||||||
([sql-map l]
|
|
||||||
(assoc sql-map :limit l)))
|
|
||||||
|
|
||||||
(defn offset
|
|
||||||
([o]
|
|
||||||
(offset {} o))
|
|
||||||
([sql-map o]
|
|
||||||
(assoc sql-map :offset o)))
|
|
||||||
|
|
||||||
(defn modifiers [& ms]
|
|
||||||
(let [[m ms] (if (map? (first ms))
|
|
||||||
[(first ms) (rest ms)]
|
|
||||||
[{} ms])]
|
|
||||||
(assoc m :modifiers ms)))
|
|
||||||
|
|
||||||
(defn merge-modifiers [sql-map & ms]
|
|
||||||
(update-in sql-map [:modifiers] concat ms))
|
|
||||||
|
|
||||||
(def ^:private handlers
|
|
||||||
{:select select
|
|
||||||
:from from
|
|
||||||
:where where
|
|
||||||
:join join
|
|
||||||
:group-by group-by
|
|
||||||
:having having
|
|
||||||
:order-by order-by
|
|
||||||
:limit limit
|
|
||||||
:offset offset
|
|
||||||
:modifiers modifiers})
|
|
||||||
|
|
||||||
(def ^:private merge-handlers
|
|
||||||
{:select merge-select
|
|
||||||
:from merge-from
|
|
||||||
:where merge-where
|
|
||||||
:join merge-join
|
|
||||||
:group-by merge-group-by
|
|
||||||
:having merge-having
|
|
||||||
:order-by merge-order-by
|
|
||||||
:limit limit
|
|
||||||
:offset offset
|
|
||||||
:modifiers merge-modifiers})
|
|
||||||
|
|
||||||
(defn- build-sql [handlers clauses]
|
|
||||||
(let [[base clauses] (if (map? (first clauses))
|
(let [[base clauses] (if (map? (first clauses))
|
||||||
[(first clauses) (rest clauses)]
|
[(first clauses) (rest clauses)]
|
||||||
[{} clauses])]
|
[{} clauses])]
|
||||||
(reduce
|
(reduce
|
||||||
(fn [sql-map [op args]]
|
(fn [sql-map [op args]]
|
||||||
(if-let [handler (handlers op)]
|
(build-clause op sql-map args))
|
||||||
(if (or (#{:where :having} op) (not (coll? args)))
|
|
||||||
(if (nil? args)
|
|
||||||
sql-map
|
|
||||||
(handler sql-map args))
|
|
||||||
(apply handler sql-map args))
|
|
||||||
sql-map))
|
|
||||||
base
|
base
|
||||||
(partition 2 clauses))))
|
(partition 2 clauses))))
|
||||||
|
|
||||||
(defn sql [& clauses]
|
|
||||||
(build-sql handlers clauses))
|
|
||||||
|
|
||||||
(defn merge-sql [& clauses]
|
|
||||||
(build-sql merge-handlers clauses))
|
|
||||||
|
|
|
||||||
136
src/honeysql/helpers.clj
Normal file
136
src/honeysql/helpers.clj
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
(ns honeysql.helpers)
|
||||||
|
|
||||||
|
(defmulti build-clause (fn [name & args]
|
||||||
|
name))
|
||||||
|
|
||||||
|
(defmethod build-clause :default [_ m & args]
|
||||||
|
m)
|
||||||
|
|
||||||
|
(defmacro defhelper [helper arglist & more]
|
||||||
|
(let [kw (keyword (name helper))]
|
||||||
|
`(do
|
||||||
|
(defmethod build-clause ~kw ~(into ['_] arglist) ~@more)
|
||||||
|
(defn ~helper [& args#]
|
||||||
|
(let [[m# args#] (if (map? (first args#))
|
||||||
|
[(first args#) (rest args#)]
|
||||||
|
[{} args#])]
|
||||||
|
(build-clause ~kw m# args#))))))
|
||||||
|
|
||||||
|
(defn collify [x]
|
||||||
|
(if (coll? x) x [x]))
|
||||||
|
|
||||||
|
(defhelper select [m fields]
|
||||||
|
(assoc m :select (collify fields)))
|
||||||
|
|
||||||
|
(defhelper merge-select [m fields]
|
||||||
|
(update-in m [:select] concat (collify fields)))
|
||||||
|
|
||||||
|
(defhelper un-select [m fields]
|
||||||
|
(update-in m [:select] #(remove (set (collify fields)) %)))
|
||||||
|
|
||||||
|
(defhelper from [m tables]
|
||||||
|
(assoc m :from (collify tables)))
|
||||||
|
|
||||||
|
(defhelper merge-from [m tables]
|
||||||
|
(update-in m [:from] concat (collify tables)))
|
||||||
|
|
||||||
|
(defmethod build-clause :where [_ m pred]
|
||||||
|
(if (nil? pred)
|
||||||
|
m
|
||||||
|
(assoc m :where pred)))
|
||||||
|
|
||||||
|
(defn- prep-where [args]
|
||||||
|
(let [[m preds] (if (map? (first args))
|
||||||
|
[(first args) (rest args)]
|
||||||
|
[{} args])
|
||||||
|
[logic-op preds] (if (keyword? (first preds))
|
||||||
|
[(first preds) (rest preds)]
|
||||||
|
[:and preds])
|
||||||
|
pred (if (= 1 (count preds))
|
||||||
|
(first preds)
|
||||||
|
(into [logic-op] preds))]
|
||||||
|
[m pred logic-op]))
|
||||||
|
|
||||||
|
(defn where [& args]
|
||||||
|
(let [[m pred] (prep-where args)]
|
||||||
|
(if (nil? pred)
|
||||||
|
m
|
||||||
|
(assoc m :where pred))))
|
||||||
|
|
||||||
|
(defmethod build-clause :merge-where [_ m pred]
|
||||||
|
(if (nil? pred)
|
||||||
|
m
|
||||||
|
(assoc m :where (if (not (nil? (:where m)))
|
||||||
|
[:and (:where m) pred]
|
||||||
|
pred))))
|
||||||
|
|
||||||
|
(defn merge-where [& args]
|
||||||
|
(let [[m pred logic-op] (prep-where args)]
|
||||||
|
(if (nil? pred)
|
||||||
|
m
|
||||||
|
(assoc m :where (if (not (nil? (:where m)))
|
||||||
|
[logic-op (:where m) pred]
|
||||||
|
pred)))))
|
||||||
|
|
||||||
|
(defhelper join [m clauses]
|
||||||
|
(assoc m :join clauses))
|
||||||
|
|
||||||
|
(defhelper merge-join [m clauses]
|
||||||
|
(update-in m [:join] concat clauses))
|
||||||
|
|
||||||
|
(defmethod build-clause :group-by [_ m fields]
|
||||||
|
(assoc m :group-by (collify fields)))
|
||||||
|
|
||||||
|
(defn group [& args]
|
||||||
|
(let [[m fields] (if (map? (first args))
|
||||||
|
[(first args) (rest args)]
|
||||||
|
[{} args])]
|
||||||
|
(build-clause :group-by m fields)))
|
||||||
|
|
||||||
|
(defhelper merge-group-by [m fields]
|
||||||
|
(update-in m [:group-by] concat (collify fields)))
|
||||||
|
|
||||||
|
(defmethod build-clause :having [_ m pred]
|
||||||
|
(if (nil? pred)
|
||||||
|
m
|
||||||
|
(assoc m :having pred)))
|
||||||
|
|
||||||
|
(defn having [& args]
|
||||||
|
(let [[m pred] (prep-where args)]
|
||||||
|
(if (nil? pred)
|
||||||
|
m
|
||||||
|
(assoc m :having pred))))
|
||||||
|
|
||||||
|
(defmethod build-clause :merge-having [_ m pred]
|
||||||
|
(if (nil? pred)
|
||||||
|
m
|
||||||
|
(assoc m :having (if (not (nil? (:having m)))
|
||||||
|
[:and (:having m) pred]
|
||||||
|
pred))))
|
||||||
|
|
||||||
|
(defn merge-having [& args]
|
||||||
|
(let [[m pred logic-op] (prep-where args)]
|
||||||
|
(if (nil? pred)
|
||||||
|
m
|
||||||
|
(assoc m :having (if (not (nil? (:having m)))
|
||||||
|
[logic-op (:having m) pred]
|
||||||
|
pred)))))
|
||||||
|
|
||||||
|
(defhelper order-by [m fields]
|
||||||
|
(assoc m :order-by (collify fields)))
|
||||||
|
|
||||||
|
(defhelper merge-order-by [m fields]
|
||||||
|
(update-in m [:order-by] concat (collify fields)))
|
||||||
|
|
||||||
|
(defhelper limit [m l]
|
||||||
|
(assoc m :limit (if (coll? l) (first l) l)))
|
||||||
|
|
||||||
|
(defhelper offset [m o]
|
||||||
|
(assoc m :offset (if (coll? o) (first o) o)))
|
||||||
|
|
||||||
|
(defhelper modifiers [m ms]
|
||||||
|
(assoc m :modifiers (collify ms)))
|
||||||
|
|
||||||
|
(defhelper merge-modifiers [m ms]
|
||||||
|
(update-in m [:modifiers] concat (collify ms)))
|
||||||
|
|
||||||
|
|
@ -1,28 +1,32 @@
|
||||||
(ns honeysql.core-test
|
(ns honeysql.core-test
|
||||||
(:refer-clojure :exclude [format group-by])
|
(:refer-clojure :exclude [format])
|
||||||
(:require [clojure.test :refer [deftest testing is]]
|
(:require [clojure.test :refer [deftest testing is]]
|
||||||
[honeysql.core :refer :all]))
|
[honeysql.core :refer :all]
|
||||||
|
[honeysql.helpers :refer :all]))
|
||||||
|
|
||||||
;; TODO: more tests
|
;; TODO: more tests
|
||||||
|
|
||||||
(deftest test-select
|
(deftest test-select
|
||||||
(let [sqlmap (-> (select :f.* :b.baz :c.quux (call :now) (raw "@x := 10"))
|
(let [m1 (-> (select :f.* :b.baz :c.quux [:b.bla "bla-bla"]
|
||||||
|
(call :now) (raw "@x := 10"))
|
||||||
|
(un-select :c.quux)
|
||||||
(modifiers :distinct)
|
(modifiers :distinct)
|
||||||
(from [:foo :f] [:baz :b])
|
(from [:foo :f] [:baz :b])
|
||||||
(join [[:clod :c] [:= :f.a :c.d] :left]
|
(join [[:clod :c] [:= :f.a :c.d] :left]
|
||||||
[:draq [:= :f.b :draq.x]])
|
[:draq [:= :f.b :draq.x]])
|
||||||
(where [:or
|
(where [:or
|
||||||
[:and [:= :f.a "bort"] [:not= :b.baz "gabba"]]
|
[:and [:= :f.a "bort"] [:not= :b.baz "gabba"]]
|
||||||
|
[:< 1 2 3]
|
||||||
[:in :f.e [1 2 3]]
|
[:in :f.e [1 2 3]]
|
||||||
[:between :f.e 10 20]])
|
[:between :f.e 10 20]])
|
||||||
(group-by :f.a)
|
(merge-where [:not= nil :b.bla])
|
||||||
|
(group :f.a)
|
||||||
(having [:< 0 :f.e])
|
(having [:< 0 :f.e])
|
||||||
(order-by [:b.baz :desc] :c.quux)
|
(order-by [:b.baz :desc] :c.quux)
|
||||||
(limit 50)
|
(limit 50)
|
||||||
(offset 10))]
|
(offset 10))]
|
||||||
(testing "sqlmap formats correctly"
|
(testing "SQL data formats correctly"
|
||||||
(is (= (format sqlmap)
|
(is (= (format m1)
|
||||||
["SELECT DISTINCT f.*, b.baz, c.quux, NOW(), @x := 10 FROM foo AS f, baz AS b LEFT JOIN clod AS c ON (f.a = c.d) JOIN draq ON (f.b = draq.x) WHERE (((f.a = ?) AND (b.baz != ?)) OR (f.e IN (1, 2, 3)) OR f.e BETWEEN 10 AND 20) GROUP BY f.a HAVING (0 < f.e) ORDER BY b.baz DESC, c.quux LIMIT 50 OFFSET 10"
|
["SELECT DISTINCT f.*, b.baz, b.bla AS \"bla-bla\", NOW(), @x := 10 FROM foo AS f, baz AS b LEFT JOIN clod AS c ON f.a = c.d JOIN draq ON f.b = draq.x WHERE (((f.a = ? AND b.baz <> ?) OR (1 < 2 AND 2 < 3) OR (f.e IN (1, 2, 3)) OR f.e BETWEEN 10 AND 20) AND b.bla IS NOT NULL) GROUP BY f.a HAVING 0 < f.e ORDER BY b.baz DESC, c.quux LIMIT 50 OFFSET 10" "bort" "gabba"])))
|
||||||
"bort" "gabba"])))
|
(testing "SQL data prints and reads correctly"
|
||||||
(testing "sqlmap prints and reads correctly"
|
(is (= m1 (read-string (pr-str m1)))))))
|
||||||
(is (= sqlmap (read-string (pr-str sqlmap)))))))
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue