Flesh out more SQL generators

Add high-level insert!, insert-multi!, update!, delete!

Add more examples to tests.
This commit is contained in:
Sean Corfield 2019-03-31 19:30:01 -07:00
parent 561ccfc621
commit 8646472e79
3 changed files with 107 additions and 6 deletions

View file

@ -6,6 +6,7 @@
[next.jdbc.prepare :as prepare] ; used to extend protocols
[next.jdbc.protocols :as p]
[next.jdbc.result-set :as rs]
[next.jdbc.sql :as sql]
[next.jdbc.transaction])) ; used to extend protocols
(set! *warn-on-reflection* true)
@ -45,3 +46,55 @@
(defmacro with-transaction
[[sym connectable opts] & body]
`(p/-transact ~connectable (fn [~sym] ~@body) ~opts))
(defn insert!
""
([connectable table key-map]
(rs/execute! connectable
(sql/for-insert table key-map {})
{:return-keys true}))
([connectable table key-map opts]
(rs/execute! connectable
(sql/for-insert table key-map opts)
(merge {:return-keys true} opts))))
(defn insert-multi!
""
([connectable table cols rows]
(rs/execute! connectable
(sql/for-insert-multi table cols rows {})
{:return-keys true}))
([connectable table cols rows opts]
(rs/execute! connectable
(sql/for-insert-multi table cols rows opts)
(merge {:return-keys true} opts))))
(defn find-by-keys
""
([connectable table key-map]
(rs/execute! connectable (sql/for-query table key-map {}) {}))
([connectable table key-map opts]
(rs/execute! connectable (sql/for-query table key-map opts) opts)))
(defn get-by-id
""
([connectable table pk]
(rs/execute-one! connectable (sql/for-query table {:id pk} {}) {}))
([connectable table pk opts]
(rs/execute-one! connectable (sql/for-query table {:id pk} opts) opts))
([connectable table pk pk-name opts]
(rs/execute-one! connectable (sql/for-query table {pk-name pk} opts) opts)))
(defn update!
""
([connectable table key-map where-params]
(rs/execute! connectable (sql/for-update table key-map where-params {}) {}))
([connectable table key-map where-params opts]
(rs/execute! connectable (sql/for-update table key-map where-params opts) opts)))
(defn delete!
""
([connectable table where-params]
(rs/execute! connectable (sql/for-delete table where-params {}) {}))
([connectable table where-params opts]
(rs/execute! connectable (sql/for-delete table where-params opts) opts)))

View file

@ -36,13 +36,28 @@
(defn for-query
""
[table key-map opts]
[table where-params opts]
(let [entity-fn (:entities opts identity)
where-params (by-keys key-map :where opts)]
where-params (if (map? where-params)
(by-keys where-params :where opts)
(into [(str "WHERE " (first where-params))]
(rest where-params)))]
(into [(str "SELECT * FROM " (entity-fn (name table))
" " (first where-params))]
(rest where-params))))
(defn for-delete
""
[table where-params opts]
(let [entity-fn (:entities opts identity)
where-params (if (map? where-params)
(by-keys where-params :where opts)
(into [(str "WHERE " (first where-params))]
(rest where-params)))]
(into [(str "DELETE FROM " (entity-fn (name table))
" " (first where-params))]
(rest where-params))))
(defn for-update
""
[table key-map where-params opts]
@ -69,13 +84,36 @@
" VALUES (" places ")")]
(vals key-map))))
(defn for-insert-multi
""
[table cols rows opts]
(assert (apply = (count cols) (map count rows)))
(let [entity-fn (:entities opts identity)
params (str/join ", " (map (comp entity-fn name) cols))
places (as-? (first rows) opts)]
(into [(str "INSERT INTO " (entity-fn (name table))
" (" params ")"
" VALUES "
(str/join ", " (repeat (count rows) (str "(" places ")"))))]
cat
rows)))
(comment
(require '[next.jdbc.quoted :refer [mysql]])
(by-keys {:a nil :b 42 :c "s"} {})
(by-keys {:a nil :b 42 :c "s"} :where {})
(as-keys {:a nil :b 42 :c "s"} {})
(as-? {:a nil :b 42 :c "s"} {})
(for-query :user {:id 9} {:entities mysql})
(for-query :user {:id nil} {:entities mysql})
(for-query :user ["id = ? and opt is null" 9] {:entities mysql})
(for-delete :user {:opt nil :id 9} {:entities mysql})
(for-delete :user ["id = ? and opt is null" 9] {:entities mysql})
(for-update :user {:status 42} {} {:entities mysql})
(for-update :user {:status 42} {:id 9} {:entities mysql})
(for-update :user {:status 42} ["id = ?" 9] {:entities mysql})
(for-insert :user {:id 9 :status 42 :opt nil} {:entities mysql}))
(for-update :user {:status 42, :opt nil} ["id = ?" 9] {:entities mysql})
(for-insert :user {:id 9 :status 42 :opt nil} {:entities mysql})
(for-insert-multi :user [:id :status]
[[42 "hello"]
[35 "world"]
[64 "dollars"]]
{:entities mysql}))

View file

@ -1,6 +1,7 @@
(ns next.jdbc-test
(:require [clojure.test :refer [deftest is testing]]
[next.jdbc :refer :all]))
[next.jdbc :refer :all]
[next.jdbc.result-set :as rs]))
(deftest a-test
(testing "FIXME, I fail."
@ -19,6 +20,12 @@
;; h2
(execute! con ["CREATE TABLE fruit (id int default 0, name varchar(32) primary key, appearance varchar(32), cost int, grade real)"])
(execute! con ["INSERT INTO fruit (id,name,appearance,cost,grade) VALUES (1,'Apple','red',59,87), (2,'Banana','yellow',29,92.2), (3,'Peach','fuzzy',139,90.0), (4,'Orange','juicy',89,88.6)"])
(insert-multi! con :fruit [:id :name :appearance :cost :grade]
[[1 "Apple" "red" 59 87]
[2,"Banana","yellow",29,92.2]
[3,"Peach","fuzzy",139,90.0]
[4,"Orange","juicy",89,88.6]]
{:return-keys false})
;; mysql
(execute! con ["CREATE TABLE fruit (id int auto_increment, name varchar(32), appearance varchar(32), cost int, grade real, primary key (id))"])
(execute! con ["INSERT INTO fruit (id,name,appearance,cost,grade) VALUES (1,'Apple','red',59,87), (2,'Banana','yellow',29,92.2), (3,'Peach','fuzzy',139,90.0), (4,'Orange','juicy',89,88.6)"]
@ -58,6 +65,7 @@
(execute! con ["select * from fruit"])
(into [] (map (partial into {})) (reducible! con ["select * from fruit"]))
(into [] (map (rs/datafiable-row con {})) (reducible! con ["select * from fruit"]))
;; with a prepopulated prepared statement
(with-open [ps (prepare con ["select * from fruit where appearance = ?" "red"] {})]
@ -101,5 +109,7 @@
(execute! t ["INSERT INTO fruit (id,name,appearance,cost,grade) VALUES (5,'Pear','green',49,47)"])
(execute! t ["select * from fruit where name = ?" "Pear"]))
(execute! con ["select * from fruit where name = ?" "Pear"])
(delete! con :fruit {:id 1})
(update! con :fruit {:appearance "Brown"} {:name "Banana"})
(execute! con ["select * from membership"]))