From 8646472e79e526193fef414cd8b9ffe7b0b46bd6 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sun, 31 Mar 2019 19:30:01 -0700 Subject: [PATCH] Flesh out more SQL generators Add high-level insert!, insert-multi!, update!, delete! Add more examples to tests. --- src/next/jdbc.clj | 53 +++++++++++++++++++++++++++++++++++++++++ src/next/jdbc/sql.clj | 48 +++++++++++++++++++++++++++++++++---- test/next/jdbc_test.clj | 12 +++++++++- 3 files changed, 107 insertions(+), 6 deletions(-) diff --git a/src/next/jdbc.clj b/src/next/jdbc.clj index c4bb6ff..96613a3 100644 --- a/src/next/jdbc.clj +++ b/src/next/jdbc.clj @@ -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))) diff --git a/src/next/jdbc/sql.clj b/src/next/jdbc/sql.clj index 9acad6e..ac2f780 100644 --- a/src/next/jdbc/sql.clj +++ b/src/next/jdbc/sql.clj @@ -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})) diff --git a/test/next/jdbc_test.clj b/test/next/jdbc_test.clj index 822f69f..5a1c557 100644 --- a/test/next/jdbc_test.clj +++ b/test/next/jdbc_test.clj @@ -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"]))