Add first pass of helpers

This commit is contained in:
Sean Corfield 2020-09-28 13:47:55 -07:00
parent 1fdd50d6b0
commit 6d31c4839d
3 changed files with 173 additions and 109 deletions

View file

@ -1,6 +0,0 @@
;; copyright (c) 2020 sean corfield, all rights reserved
(ns honey.sql.helpers
"Macros to create consistent helpers from DSL clauses.
I don't know how this will work in ClojureScript yet...")

View file

@ -0,0 +1,67 @@
;; copyright (c) 2020 sean corfield, all rights reserved
(ns honey.sql.helpers
"Helper functions for the built-in clauses in honey.sql."
(:refer-clojure :exclude [update set group-by])
(:require [honey.sql :as h]))
(defn- default-merge [current args]
(into (vec current) args))
(def ^:private special-merges
{:where (fn [current args]
(if (= :and (first (first args)))
(default-merge current args)
(-> [:and]
(into current)
(into args))))})
(defn- helper-merge [data k args]
(let [merge-fn (special-merges k default-merge)]
(clojure.core/update data k merge-fn args)))
(defn- generic [k args]
(if (map? (first args))
(let [[data & args] args]
(helper-merge data k args))
(helper-merge {} k args)))
(defn with [& args] (generic :with args))
(defn with-recursive [& args] (generic :with-recursive args))
(defn intersect [& args] (generic :intersect args))
(defn union [& args] (generic :union args))
(defn union-all [& args] (generic :union-all args))
(defn except [& args] (generic :except args))
(defn except-all [& args] (generic :except-all args))
(defn select [& args] (generic :select args))
(defn insert-into [& args] (generic :insert-into args))
(defn update [& args] (generic :update args))
(defn delete [& args] (generic :delete args))
(defn delete-from [& args] (generic :delete-from args))
(defn truncate [& args] (generic :truncate args))
(defn columns [& args] (generic :columns args))
(defn set [& args] (generic :set args))
(defn from [& args] (generic :from args))
(defn join [& args] (generic :join args))
(defn left-join [& args] (generic :left-join args))
(defn right-join [& args] (generic :right-join args))
(defn inner-join [& args] (generic :inner-join args))
(defn outer-join [& args] (generic :outer-join args))
(defn full-join [& args] (generic :full-join args))
(defn cross-join [& args] (generic :cross-join args))
(defn where [& args] (generic :where args))
(defn group-by [& args] (generic :group-by args))
(defn having [& args] (generic :having args))
(defn order-by [& args] (generic :order-by args))
(defn limit [& args] (generic :limit args))
(defn offset [& args] (generic :offset args))
(defn values [& args] (generic :values args))
(defn on-conflict [& args] (generic :on-conflict args))
(defn on-constraint [& args] (generic :on-constraint args))
(defn do-nothing [& args] (generic :do-nothing args))
(defn do-update-set [& args] (generic :do-update-set args))
(defn returning [& args] (generic :returning args))
#?(:clj
(assert (= (clojure.core/set @@#'h/base-clause-order)
(clojure.core/set (map keyword (keys (ns-publics *ns*)))))))

View file

@ -22,16 +22,14 @@
(seancorfield.readme/defreadme readme-25
(seancorfield.readme/defreadme readme-27
(require '[honey.sql :as sql] (require '[honey.sql :as sql]
'[honey.sql.helpers :refer :all :as helpers]) '[honey.sql.helpers :refer :all :as helpers])
) )
(seancorfield.readme/defreadme readme-34 (seancorfield.readme/defreadme readme-32
(def sqlmap {:select [:a :b :c] (def sqlmap {:select [:a :b :c]
:from [:foo] :from [:foo]
:where [:= :f.a "baz"]}) :where [:= :f.a "baz"]})
@ -43,7 +41,7 @@
(seancorfield.readme/defreadme readme-46 (seancorfield.readme/defreadme readme-44
(sql/format sqlmap) (sql/format sqlmap)
=> ["SELECT a, b, c FROM foo WHERE f.a = ?" "baz"] => ["SELECT a, b, c FROM foo WHERE f.a = ?" "baz"]
) )
@ -64,7 +62,7 @@
(seancorfield.readme/defreadme readme-67 (seancorfield.readme/defreadme readme-65
(def q-sqlmap {:select [:foo/a :foo/b :foo/c] (def q-sqlmap {:select [:foo/a :foo/b :foo/c]
:from [:foo] :from [:foo]
:where [:= :foo/a "baz"]}) :where [:= :foo/a "baz"]})
@ -78,7 +76,7 @@
(seancorfield.readme/defreadme readme-81 (seancorfield.readme/defreadme readme-79
(-> (select :a :b :c) (-> (select :a :b :c)
(from :foo) (from :foo)
(where [:= :f.a "baz"])) (where [:= :f.a "baz"]))
@ -86,7 +84,7 @@
(seancorfield.readme/defreadme readme-89 (seancorfield.readme/defreadme readme-87
(= (-> (select :*) (from :foo)) (= (-> (select :*) (from :foo))
(-> (from :foo) (select :*))) (-> (from :foo) (select :*)))
=> true => true
@ -94,14 +92,14 @@
(seancorfield.readme/defreadme readme-97 (seancorfield.readme/defreadme readme-95
(-> sqlmap (select :d)) (-> sqlmap (select :d))
=> '{:from [:foo], :where [:= :f.a "baz"], :select [:a :b :c :d]} => '{:from [:foo], :where [:= :f.a "baz"], :select [:a :b :c :d]}
) )
(seancorfield.readme/defreadme readme-104 (seancorfield.readme/defreadme readme-102
(-> sqlmap (-> sqlmap
(dissoc :select) (dissoc :select)
(select :*) (select :*)
@ -112,7 +110,7 @@
(seancorfield.readme/defreadme readme-115 (seancorfield.readme/defreadme readme-113
(-> (select :*) (-> (select :*)
(from :foo) (from :foo)
(where [:= :a 1] [:< :b 100]) (where [:= :a 1] [:< :b 100])
@ -123,7 +121,7 @@
(seancorfield.readme/defreadme readme-126 (seancorfield.readme/defreadme readme-124
(-> (select :a [:b :bar] :c [:d :x]) (-> (select :a [:b :bar] :c [:d :x])
(from [:foo :quux]) (from [:foo :quux])
(where [:= :quux.a 1] [:< :bar 100]) (where [:= :quux.a 1] [:< :bar 100])
@ -140,17 +138,18 @@
(seancorfield.readme/defreadme readme-143 (seancorfield.readme/defreadme readme-141
(-> (insert-into :properties) (-> (insert-into :properties)
(columns :name :surname :age) (columns :name :surname :age)
(values (values
[["Jon" "Smith" 34] [["Jon" "Smith" 34]
["Andrew" "Cooper" 12] ["Andrew" "Cooper" 12]
["Jane" "Daniels" 56]]) ["Jane" "Daniels" 56]])
sql/format) (sql/format {:pretty? true}))
=> [#sql/regularize => ["
"INSERT INTO properties (name, surname, age) INSERT INTO properties (name, surname, age)
VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?)" VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?)
"
"Jon" "Smith" 34 "Andrew" "Cooper" 12 "Jane" "Daniels" 56] "Jon" "Smith" 34 "Andrew" "Cooper" 12 "Jane" "Daniels" 56]
) )
@ -158,15 +157,16 @@
(seancorfield.readme/defreadme readme-161 (seancorfield.readme/defreadme readme-160
(-> (insert-into :properties) (-> (insert-into :properties)
(values [{:name "John" :surname "Smith" :age 34} (values [{:name "John" :surname "Smith" :age 34}
{:name "Andrew" :surname "Cooper" :age 12} {:name "Andrew" :surname "Cooper" :age 12}
{:name "Jane" :surname "Daniels" :age 56}]) {:name "Jane" :surname "Daniels" :age 56}])
sql/format) (sql/format {:pretty? true}))
=> [#sql/regularize => ["
"INSERT INTO properties (name, surname, age) INSERT INTO properties (name, surname, age)
VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?)" VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?)
"
"John" "Smith" 34 "John" "Smith" 34
"Andrew" "Cooper" 12 "Andrew" "Cooper" 12
"Jane" "Daniels" 56] "Jane" "Daniels" 56]
@ -184,16 +184,17 @@
:role_id (-> (select :id) :role_id (-> (select :id)
(from :role) (from :role)
(where [:= :name role-name]))}]) (where [:= :name role-name]))}])
sql/format)) (sql/format {:pretty? true})))
=> [#sql/regularize => ["
"INSERT INTO user_profile_to_role (user_profile_id, role_id) INSERT INTO user_profile_to_role (user_profile_id, role_id)
VALUES (?, (SELECT id FROM role WHERE name = ?))" VALUES (?, (SELECT id FROM role WHERE name = ?))
"
12345 12345
"user"] "user"]
) )
(seancorfield.readme/defreadme readme-196 (seancorfield.readme/defreadme readme-197
(-> (select :*) (-> (select :*)
(from :foo) (from :foo)
(where [:in :foo.a (-> (select :a) (from :bar))]) (where [:in :foo.a (-> (select :a) (from :bar))])
@ -205,16 +206,17 @@
(seancorfield.readme/defreadme readme-208 (seancorfield.readme/defreadme readme-209
(-> (insert-into :comp_table) (-> (insert-into :comp_table)
(columns :name :comp_column) (columns :name :comp_column)
(values (values
[["small" (composite 1 "inch")] [["small" (composite 1 "inch")]
["large" (composite 10 "feet")]]) ["large" (composite 10 "feet")]])
sql/format) (sql/format {:pretty? true}))
=> [#sql/regularize => ["
"INSERT INTO comp_table (name, comp_column) INSERT INTO comp_table (name, comp_column)
VALUES (?, (?, ?)), (?, (?, ?))" VALUES (?, (?, ?)), (?, (?, ?))
"
"small" 1 "inch" "large" 10 "feet"] "small" 1 "inch" "large" 10 "feet"]
) )
@ -223,15 +225,16 @@
(seancorfield.readme/defreadme readme-226 (seancorfield.readme/defreadme readme-228
(-> (helpers/update :films) (-> (helpers/update :films)
(sset {:kind "dramatic" (sset {:kind "dramatic"
:watched (sql/call :+ :watched 1)}) :watched (sql/call :+ :watched 1)})
(where [:= :kind "drama"]) (where [:= :kind "drama"])
sql/format) (sql/format {:pretty? true}))
=> [#sql/regularize => ["
"UPDATE films SET kind = ?, watched = (watched + ?) UPDATE films SET kind = ?, watched = (watched + ?)
WHERE kind = ?" WHERE kind = ?
"
"dramatic" "dramatic"
1 1
"drama"] "drama"]
@ -248,34 +251,35 @@
(seancorfield.readme/defreadme readme-251 (seancorfield.readme/defreadme readme-254
(-> (delete-from :films) (-> (delete-from :films)
(where [:<> :kind "musical"]) (where [:<> :kind "musical"])
sql/format) (sql/format))
=> ["DELETE FROM films WHERE kind <> ?" "musical"] => ["DELETE FROM films WHERE kind <> ?" "musical"]
) )
(seancorfield.readme/defreadme readme-260 (seancorfield.readme/defreadme readme-263
(-> (delete [:films :directors]) (-> (delete [:films :directors])
(from :films) (from :films)
(join :directors [:= :films.director_id :directors.id]) (join :directors [:= :films.director_id :directors.id])
(where [:<> :kind "musical"]) (where [:<> :kind "musical"])
sql/format) (sql/format {:pretty? true}))
=> [#sql/regularize => ["
"DELETE films, directors DELETE films, directors
FROM films FROM films
INNER JOIN directors ON films.director_id = directors.id INNER JOIN directors ON films.director_id = directors.id
WHERE kind <> ?" WHERE kind <> ?
"
"musical"] "musical"]
) )
(seancorfield.readme/defreadme readme-276 (seancorfield.readme/defreadme readme-280
(-> (truncate :films) (-> (truncate :films)
sql/format) (sql/format))
=> ["TRUNCATE films"] => ["TRUNCATE films"]
) )
@ -283,7 +287,7 @@
(seancorfield.readme/defreadme readme-286 (seancorfield.readme/defreadme readme-290
(sql/format {:union [(-> (select :*) (from :foo)) (sql/format {:union [(-> (select :*) (from :foo))
(-> (select :*) (from :bar))]}) (-> (select :*) (from :bar))]})
=> ["SELECT * FROM foo UNION SELECT * FROM bar"] => ["SELECT * FROM foo UNION SELECT * FROM bar"]
@ -293,11 +297,11 @@
(seancorfield.readme/defreadme readme-296 (seancorfield.readme/defreadme readme-300
(-> (select :%count.*) (from :foo) sql/format) (-> (select :%count.*) (from :foo) sql/format)
=> ["SELECT count(*) FROM foo"] => ["SELECT count(*) FROM foo"]
) )
(seancorfield.readme/defreadme readme-300 (seancorfield.readme/defreadme readme-304
(-> (select :%max.id) (from :foo) sql/format) (-> (select :%max.id) (from :foo) sql/format)
=> ["SELECT max(id) FROM foo"] => ["SELECT max(id) FROM foo"]
) )
@ -308,7 +312,7 @@
(seancorfield.readme/defreadme readme-311 (seancorfield.readme/defreadme readme-315
(-> (select :id) (-> (select :id)
(from :foo) (from :foo)
(where [:= :a :?baz]) (where [:= :a :?baz])
@ -321,19 +325,19 @@
(seancorfield.readme/defreadme readme-324 (seancorfield.readme/defreadme readme-328
(def call-qualify-map (def call-qualify-map
(-> (select [[:foo :bar]] [[:raw "@var := foo.bar"]]) (-> (select [[:foo :bar]] [[:raw "@var := foo.bar"]])
(from :foo) (from :foo)
(where [:= :a (???/param :baz)] [:= :b [:inline 42]]))) (where [:= :a (???/param :baz)] [:= :b [:inline 42]])))
) )
(seancorfield.readme/defreadme readme-330 (seancorfield.readme/defreadme readme-334
call-qualify-map call-qualify-map
=> '{:where [:and [:= :a ???/param :baz] [:= :b [:inline 42]]] => '{:where [:and [:= :a ???/param :baz] [:= :b [:inline 42]]]
:from (:foo) :from (:foo)
:select [[[:foo :bar]] [[:raw "@var := foo.bar"]]]} :select [[[:foo :bar]] [[:raw "@var := foo.bar"]]]}
) )
(seancorfield.readme/defreadme readme-336 (seancorfield.readme/defreadme readme-340
(sql/format call-qualify-map :??? {:baz "BAZ"}) (sql/format call-qualify-map :??? {:baz "BAZ"})
=> ["SELECT foo(bar), @var := foo.bar FROM foo WHERE (a = ?) AND (b = 42)" "BAZ"] => ["SELECT foo(bar), @var := foo.bar FROM foo WHERE (a = ?) AND (b = 42)" "BAZ"]
) )
@ -343,15 +347,16 @@ call-qualify-map
(seancorfield.readme/defreadme readme-346 (seancorfield.readme/defreadme readme-350
(-> (insert-into :sample) (-> (insert-into :sample)
(values [{:location [:ST_SetSRID (values [{:location [:ST_SetSRID
[:ST_MakePoint 0.291 32.621] [:ST_MakePoint 0.291 32.621]
[:cast 4325 :integer]]}]) [:cast 4325 :integer]]}])
(sql/format)) (sql/format {:pretty? true}))
=> [#sql/regularize => ["
"INSERT INTO sample (location) INSERT INTO sample (location)
VALUES (ST_SetSRID(ST_MakePoint(?, ?), CAST(? AS integer)))" VALUES (ST_SetSRID(ST_MakePoint(?, ?), CAST(? AS integer)))
"
0.291 32.621 4326] 0.291 32.621 4326]
) )
@ -368,7 +373,7 @@ call-qualify-map
(seancorfield.readme/defreadme readme-371 (seancorfield.readme/defreadme readme-376
(-> (select :*) (-> (select :*)
(from :foo) (from :foo)
(where [:< :expired_at (sql/raw ["now() - '" 5 " seconds'"])]) (where [:< :expired_at (sql/raw ["now() - '" 5 " seconds'"])])
@ -376,7 +381,7 @@ call-qualify-map
=> ["SELECT * FROM foo WHERE expired_at < now() - '? seconds'" 5] => ["SELECT * FROM foo WHERE expired_at < now() - '? seconds'" 5]
) )
(seancorfield.readme/defreadme readme-379 (seancorfield.readme/defreadme readme-384
(-> (select :*) (-> (select :*)
(from :foo) (from :foo)
(where [:< :expired_at (sql/raw ["now() - '" #sql/param :t " seconds'"])]) (where [:< :expired_at (sql/raw ["now() - '" #sql/param :t " seconds'"])])
@ -393,7 +398,7 @@ call-qualify-map
(seancorfield.readme/defreadme readme-396 (seancorfield.readme/defreadme readme-401
(-> (select :foo.a) (-> (select :foo.a)
(from :foo) (from :foo)
(where [:= :foo.a "baz"]) (where [:= :foo.a "baz"])
@ -409,7 +414,7 @@ call-qualify-map
(seancorfield.readme/defreadme readme-412 (seancorfield.readme/defreadme readme-417
(-> (select :foo.a) (-> (select :foo.a)
(from :foo) (from :foo)
(where [:= :foo.a "baz"]) (where [:= :foo.a "baz"])
@ -421,7 +426,7 @@ call-qualify-map
(seancorfield.readme/defreadme readme-424 (seancorfield.readme/defreadme readme-429
(sql/format (sql/format
{:select [:f.foo-id :f.foo-name] {:select [:f.foo-id :f.foo-name]
:from [[:foo-bar :f]] :from [[:foo-bar :f]]
@ -435,7 +440,7 @@ call-qualify-map
(seancorfield.readme/defreadme readme-438 (seancorfield.readme/defreadme readme-443
(def big-complicated-map (def big-complicated-map
(-> (select :f.* :b.baz :c.quux [:b.bla "bla-bla"] (-> (select :f.* :b.baz :c.quux [:b.bla "bla-bla"]
[[:now]] [[:raw "@x := 10"]]) [[:now]] [[:raw "@x := 10"]])
@ -455,7 +460,7 @@ call-qualify-map
(limit 50) (limit 50)
(offset 10))) (offset 10)))
) )
(seancorfield.readme/defreadme readme-458 (seancorfield.readme/defreadme readme-463
big-complicated-map big-complicated-map
=> {:select [:f.* :b.baz :c.quux [:b.bla "bla-bla"] => {:select [:f.* :b.baz :c.quux [:b.bla "bla-bla"]
[[:now]] [[:raw "@x := 10"]]] [[:now]] [[:raw "@x := 10"]]]
@ -475,26 +480,24 @@ big-complicated-map
:limit 50 :limit 50
:offset 10} :offset 10}
) )
(seancorfield.readme/defreadme readme-478 (seancorfield.readme/defreadme readme-483
(sql/format big-complicated-map {:param1 "gabba" :param2 2}) (sql/format big-complicated-map {:param1 "gabba" :param2 2})
=> [#sql/regularize => ["
"SELECT DISTINCT f.*, b.baz, c.quux, b.bla AS bla_bla, now(), @x := 10 SELECT DISTINCT f.*, b.baz, c.quux, b.bla AS bla_bla, now(), @x := 10
FROM foo f, baz b FROM foo f, baz b
INNER JOIN draq ON f.b = draq.x INNER JOIN draq ON f.b = draq.x
LEFT JOIN clod c ON f.a = c.d LEFT JOIN clod c ON f.a = c.d
RIGHT JOIN bock ON bock.z = c.e RIGHT JOIN bock ON bock.z = c.e
WHERE ((f.a = ? AND b.baz <> ?) WHERE ((f.a = ? AND b.baz <> ?) OR (? < ? AND ? < ?) OR (f.e in (?, ?, ?)) OR f.e BETWEEN ? AND ?)
OR (? < ? AND ? < ?)
OR (f.e in (?, ?, ?))
OR f.e BETWEEN ? AND ?)
GROUP BY f.a, c.e GROUP BY f.a, c.e
HAVING ? < f.e HAVING ? < f.e
ORDER BY b.baz DESC, c.quux, f.a NULLS FIRST ORDER BY b.baz DESC, c.quux, f.a NULLS FIRST
LIMIT ? LIMIT ?
OFFSET ? " OFFSET ?
"
"bort" "gabba" 1 2 2 3 1 2 3 10 20 0 50 10] "bort" "gabba" 1 2 2 3 1 2 3 10 20 0 50 10]
) )
(seancorfield.readme/defreadme readme-497 (seancorfield.readme/defreadme readme-500
;; Printable and readable ;; Printable and readable
(= big-complicated-map (read-string (pr-str big-complicated-map))) (= big-complicated-map (read-string (pr-str big-complicated-map)))
=> true => true
@ -506,7 +509,7 @@ big-complicated-map
(seancorfield.readme/defreadme readme-509 (seancorfield.readme/defreadme readme-512
(defmethod fmt/fn-handler "betwixt" [_ field lower upper] (defmethod fmt/fn-handler "betwixt" [_ field lower upper]
(str (fmt/to-sql field) " BETWIXT " (str (fmt/to-sql field) " BETWIXT "
(fmt/to-sql lower) " AND " (fmt/to-sql upper))) (fmt/to-sql lower) " AND " (fmt/to-sql upper)))
@ -517,23 +520,23 @@ big-complicated-map
(seancorfield.readme/defreadme readme-520 (seancorfield.readme/defreadme readme-523
;; Takes a MapEntry of the operator & clause data, plus the entire SQL map ;; Takes a MapEntry of the operator & clause data, plus the entire SQL map
(defmethod fmt/format-clause :foobar [[op v] sqlmap] (defmethod fmt/format-clause :foobar [[op v] sqlmap]
(str "FOOBAR " (fmt/to-sql v))) (str "FOOBAR " (fmt/to-sql v)))
) )
(seancorfield.readme/defreadme readme-525 (seancorfield.readme/defreadme readme-528
(sql/format {:select [:a :b] :foobar :baz}) (sql/format {:select [:a :b] :foobar :baz})
=> ["SELECT a, b FOOBAR baz"] => ["SELECT a, b FOOBAR baz"]
) )
(seancorfield.readme/defreadme readme-529 (seancorfield.readme/defreadme readme-532
(require '[honeysql.helpers :refer [defhelper]]) (require '[honeysql.helpers :refer [defhelper]])
;; Defines a helper function, and allows 'build' to recognize your clause ;; Defines a helper function, and allows 'build' to recognize your clause
(defhelper foobar [m args] (defhelper foobar [m args]
(assoc m :foobar (first args))) (assoc m :foobar (first args)))
) )
(seancorfield.readme/defreadme readme-536 (seancorfield.readme/defreadme readme-539
(-> (select :a :b) (foobar :baz) sql/format) (-> (select :a :b) (foobar :baz) sql/format)
=> ["SELECT a, b FOOBAR baz"] => ["SELECT a, b FOOBAR baz"]
@ -541,7 +544,7 @@ big-complicated-map
(seancorfield.readme/defreadme readme-544 (seancorfield.readme/defreadme readme-547
(fmt/register-clause! :foobar 110) (fmt/register-clause! :foobar 110)
) )