Turn all the examples into tests
This commit is contained in:
parent
f691d59be7
commit
228335d2f3
1 changed files with 107 additions and 185 deletions
|
|
@ -2,190 +2,112 @@
|
||||||
|
|
||||||
(ns next.jdbc-test
|
(ns next.jdbc-test
|
||||||
"Not exactly a test suite -- more a series of examples."
|
"Not exactly a test suite -- more a series of examples."
|
||||||
(:require [clojure.string :as str]
|
(:require [clojure.test :refer [deftest is testing]]
|
||||||
[clojure.test :refer [deftest is testing]]
|
[next.jdbc :as jdbc]
|
||||||
[next.jdbc :refer :all]
|
[next.jdbc.test-fixtures :refer [with-test-db ds]]
|
||||||
[next.jdbc.result-set :as rs]
|
[next.jdbc.prepare :as prep]
|
||||||
[next.jdbc.sql :refer :all])
|
[next.jdbc.result-set :as rs])
|
||||||
(:import (java.sql ResultSet ResultSetMetaData)))
|
(:import (java.sql ResultSet ResultSetMetaData)))
|
||||||
|
|
||||||
(comment
|
(deftest basic-tests
|
||||||
(def db-spec {:dbtype "h2:mem" :dbname "clojure_test_perf"})
|
(testing "reducible!"
|
||||||
;; these should be equivalent
|
(is (= "Apple"
|
||||||
(def con (get-connection (get-datasource db-spec) {}))
|
|
||||||
(def con (get-connection db-spec {}))
|
|
||||||
(execute-one! con ["DROP TABLE fruit"])
|
|
||||||
;; h2
|
|
||||||
(execute-one! con ["CREATE TABLE fruit (id int default 0, name varchar(32) primary key, appearance varchar(32), cost int, grade real)"])
|
|
||||||
;; either this...
|
|
||||||
(execute-one! 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)"])
|
|
||||||
;; ...or this (H2 can't return generated keys for this)
|
|
||||||
(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)"]
|
|
||||||
{:return-keys true})
|
|
||||||
;; when you're done
|
|
||||||
(.close con)
|
|
||||||
|
|
||||||
(require '[criterium.core :refer [bench quick-bench]])
|
|
||||||
|
|
||||||
(require '[clojure.java.jdbc :as jdbc])
|
|
||||||
|
|
||||||
;; calibrate
|
|
||||||
(quick-bench (reduce + (take 10e6 (range))))
|
|
||||||
|
|
||||||
;; raw java
|
|
||||||
(defn select* [^java.sql.Connection con]
|
|
||||||
(let [ps (doto (.prepareStatement con "SELECT * FROM fruit WHERE appearance = ?")
|
|
||||||
(.setObject 1 "red"))
|
|
||||||
rs (.executeQuery ps)
|
|
||||||
_ (.next rs)
|
|
||||||
value (.getObject rs "name")]
|
|
||||||
(.close ps)
|
|
||||||
value))
|
|
||||||
(quick-bench (select* con)) ; 1.14 micros
|
|
||||||
|
|
||||||
;; almost same as the Java example above -- 1.57 micros -- 1.4x Java
|
|
||||||
(quick-bench
|
|
||||||
(reduce (fn [rs m] (reduced (:name m)))
|
|
||||||
nil
|
|
||||||
(reducible! con ["select * from fruit where appearance = ?" "red"])))
|
|
||||||
;; run through convenience function -- 2.4 micros
|
|
||||||
(quick-bench
|
|
||||||
(:FRUIT/NAME (execute-one! con
|
|
||||||
["select * from fruit where appearance = ?" "red"]
|
|
||||||
{})))
|
|
||||||
|
|
||||||
;; 6.8 micros -- 3x
|
|
||||||
(quick-bench
|
|
||||||
(jdbc/query {:connection con}
|
|
||||||
["select * from fruit where appearance = ?" "red"]
|
|
||||||
{:row-fn :name :result-set-fn first}))
|
|
||||||
|
|
||||||
;; simple query -- 2.6 micros
|
|
||||||
(quick-bench
|
|
||||||
(execute! con ["select * from fruit where appearance = ?" "red"]))
|
|
||||||
|
|
||||||
;; 6.9 -- ~2.6x
|
|
||||||
(quick-bench
|
|
||||||
(jdbc/query {:connection con} ["select * from fruit where appearance = ?" "red"]))
|
|
||||||
|
|
||||||
(quick-bench ; default -- 4.55-4.57
|
|
||||||
(execute! con ["select * from fruit"] {:gen-fn rs/as-maps}))
|
|
||||||
(quick-bench ; arrays -- 4.34-4.4
|
|
||||||
(execute! con ["select * from fruit"] {:gen-fn rs/as-arrays}))
|
|
||||||
(quick-bench ; simple keys -- 4.55-4.57
|
|
||||||
(execute! con ["select * from fruit"] {:gen-fn rs/as-unqualified-maps}))
|
|
||||||
(quick-bench ; simple keys, arrays -- 4.34-4.4
|
|
||||||
(execute! con ["select * from fruit"] {:gen-fn rs/as-unqualified-arrays}))
|
|
||||||
|
|
||||||
(quick-bench ; 9.5 -- 2x
|
|
||||||
(jdbc/query {:connection con} ["select * from fruit"]))
|
|
||||||
|
|
||||||
(quick-bench ; 8.2
|
|
||||||
(with-transaction [t con {:rollback-only true}]
|
|
||||||
(execute! t ["INSERT INTO fruit (id,name,appearance,cost,grade) VALUES (5,'Pear','green',49,47)"])))
|
|
||||||
|
|
||||||
(quick-bench ; 15.7
|
|
||||||
(with-transaction [t con {:rollback-only true}]
|
|
||||||
(insert! t :fruit {:id 5, :name "Pear", :appearance "green", :cost 49, :grade 47})))
|
|
||||||
|
|
||||||
(quick-bench ; 13.6 -- 1.6x
|
|
||||||
(jdbc/with-db-transaction [t {:connection con}]
|
|
||||||
(jdbc/db-set-rollback-only! t)
|
|
||||||
(jdbc/execute! t ["INSERT INTO fruit (id,name,appearance,cost,grade) VALUES (5,'Pear','green',49,47)"])))
|
|
||||||
|
|
||||||
(quick-bench ; 27.9-28.8 -- 1.8x
|
|
||||||
(jdbc/with-db-transaction [t {:connection con}]
|
|
||||||
(jdbc/db-set-rollback-only! t)
|
|
||||||
(jdbc/insert! t :fruit {:id 5, :name "Pear", :appearance "green", :cost 49, :grade 47})))
|
|
||||||
|
|
||||||
(delete! con :fruit {:id 5})
|
|
||||||
;; with a prepopulated prepared statement - 450ns
|
|
||||||
(with-open [ps (prepare con ["select * from fruit where appearance = ?" "red"] {})]
|
|
||||||
(quick-bench
|
|
||||||
[(reduce (fn [_ row] (reduced (:name row)))
|
|
||||||
nil
|
|
||||||
(reducible! ps))]))
|
|
||||||
|
|
||||||
(require '[next.jdbc.prepare :as prepare])
|
|
||||||
|
|
||||||
;; same as above but setting parameters inside the benchmark
|
|
||||||
(with-open [ps (prepare con ["select * from fruit where appearance = ?"] {})]
|
|
||||||
(quick-bench
|
|
||||||
[(reduce (fn [_ row] (reduced (:name row)))
|
|
||||||
nil
|
|
||||||
(reducible! (prepare/set-parameters ps ["red"])))]))
|
|
||||||
|
|
||||||
;; this takes more than twice the time of the one above which seems strange
|
|
||||||
(with-open [ps (prepare con ["select * from fruit where appearance = ?"] {})]
|
|
||||||
(quick-bench
|
|
||||||
[(reduce (fn [_ row] (reduced (:name row)))
|
|
||||||
nil
|
|
||||||
(reducible! (prepare/set-parameters ps ["red"])))
|
|
||||||
(reduce (fn [_ row] (reduced (:name row)))
|
(reduce (fn [_ row] (reduced (:name row)))
|
||||||
nil
|
nil
|
||||||
(reducible! (prepare/set-parameters ps ["fuzzy"])))]))
|
(jdbc/reducible!
|
||||||
|
(ds)
|
||||||
;; full first row
|
["select * from fruit where appearance = ?" "red"])))))
|
||||||
(quick-bench
|
(testing "execute-one!"
|
||||||
(execute-one! con ["select * from fruit where appearance = ?" "red"]))
|
(is (= "Apple" (:FRUIT/NAME
|
||||||
|
(jdbc/execute-one!
|
||||||
(with-transaction [t con {:rollback-only true}]
|
(ds)
|
||||||
(insert! t :fruit {:id 5, :name "Pear", :appearance "green", :cost 49, :grade 47})
|
["select * from fruit where appearance = ?" "red"])))))
|
||||||
(query t ["select * from fruit where name = ?" "Pear"]))
|
(testing "execute!"
|
||||||
(query con ["select * from fruit where name = ?" "Pear"])
|
(let [rs (jdbc/execute!
|
||||||
|
(ds)
|
||||||
(delete! con :fruit {:id 1})
|
["select * from fruit where appearance = ?" "red"])]
|
||||||
(update! con :fruit {:appearance "Brown"} {:name "Banana"})
|
(is (= 1 (count rs)))
|
||||||
|
(is (= 1 (:FRUIT/ID (first rs)))))
|
||||||
(reduce (fn [rs m] (reduced (assoc m :test 42)))
|
(let [rs (jdbc/execute!
|
||||||
nil
|
(ds)
|
||||||
(reducible! con ["select * from fruit where appearance = ?" "red"]))
|
["select * from fruit order by id"]
|
||||||
|
{:gen-fn rs/as-maps})]
|
||||||
(reduce (fn [rs m] (reduced (rs/datafiable-row (assoc m :test 42) con {})))
|
(is (every? map? rs))
|
||||||
nil
|
(is (every? meta rs))
|
||||||
(reducible! con ["select * from fruit where appearance = ?" "red"]))
|
(is (= 4 (count rs)))
|
||||||
|
(is (= 1 (:FRUIT/ID (first rs))))
|
||||||
(reduce (fn [rs m] (reduced (rs/datafiable-row m con {})))
|
(is (= 4 (:FRUIT/ID (last rs)))))
|
||||||
nil
|
(let [rs (jdbc/execute!
|
||||||
(reducible! con ["select * from fruit where appearance = ?" "red"]))
|
(ds)
|
||||||
|
["select * from fruit order by id"]
|
||||||
(defrecord Fruit [id name appearance cost grade])
|
{:gen-fn rs/as-arrays})]
|
||||||
|
(is (every? vector? rs))
|
||||||
(defn fruit-builder [^java.sql.ResultSet rs opts]
|
(is (= 5 (count rs)))
|
||||||
(reify
|
(is (every? #(= 5 (count %)) rs))
|
||||||
rs/RowBuilder
|
;; columns come first
|
||||||
(->row [_] (->Fruit (.getObject rs "id")
|
(is (every? qualified-keyword? (first rs)))
|
||||||
(.getObject rs "name")
|
;; :FRUIT/ID should be first column
|
||||||
(.getObject rs "appearance")
|
(is (= :FRUIT/ID (ffirst rs)))
|
||||||
(.getObject rs "cost")
|
;; and all its corresponding values should be ints
|
||||||
(.getObject rs "grade")))
|
(is (every? int? (map first (rest rs))))
|
||||||
(with-column [_ row i] row)
|
(is (every? string? (map second (rest rs)))))
|
||||||
(column-count [_] 0) ; no need to iterate over columns
|
(let [rs (jdbc/execute!
|
||||||
(row! [_ row] row)
|
(ds)
|
||||||
rs/ResultSetBuilder
|
["select * from fruit order by id"]
|
||||||
(->rs [_] (transient []))
|
{:gen-fn rs/as-unqualified-maps})]
|
||||||
(with-row [_ rs row] (conj! rs row))
|
(is (every? map? rs))
|
||||||
(rs! [_ rs] (persistent! rs))))
|
(is (every? meta rs))
|
||||||
|
(is (= 4 (count rs)))
|
||||||
(quick-bench ; 2.2 micros
|
(is (= 1 (:ID (first rs))))
|
||||||
(execute-one! con ["select * from fruit where appearance = ?" "red"]
|
(is (= 4 (:ID (last rs)))))
|
||||||
{:gen-fn fruit-builder}))
|
(let [rs (jdbc/execute!
|
||||||
(quick-bench ; 2.47 micros
|
(ds)
|
||||||
(execute! con ["select * from fruit where appearance = ?" "red"]
|
["select * from fruit order by id"]
|
||||||
{:gen-fn fruit-builder}))
|
{:gen-fn rs/as-unqualified-arrays})]
|
||||||
(quick-bench ; 3 micros
|
(is (every? vector? rs))
|
||||||
(execute! con ["select * from fruit"]
|
(is (= 5 (count rs)))
|
||||||
{:gen-fn fruit-builder}))
|
(is (every? #(= 5 (count %)) rs))
|
||||||
|
;; columns come first
|
||||||
;; with a prepopulated prepared statement - 1.7-1.8 micros
|
(is (every? simple-keyword? (first rs)))
|
||||||
(with-open [ps (prepare con ["select * from fruit"] {})]
|
;; :ID should be first column
|
||||||
(quick-bench
|
(is (= :ID (ffirst rs)))
|
||||||
(execute! ps [] {:gen-fn fruit-builder}))))
|
;; and all its corresponding values should be ints
|
||||||
|
(is (every? int? (map first (rest rs))))
|
||||||
|
(is (every? string? (map second (rest rs))))))
|
||||||
|
(testing "prepare"
|
||||||
|
(let [rs (with-open [con (jdbc/get-connection (ds))]
|
||||||
|
(with-open [ps (jdbc/prepare
|
||||||
|
con
|
||||||
|
["select * from fruit order by id"])]
|
||||||
|
(jdbc/execute! ps)))]
|
||||||
|
(is (every? map? rs))
|
||||||
|
(is (every? meta rs))
|
||||||
|
(is (= 4 (count rs)))
|
||||||
|
(is (= 1 (:FRUIT/ID (first rs))))
|
||||||
|
(is (= 4 (:FRUIT/ID (last rs)))))
|
||||||
|
(let [rs (with-open [con (jdbc/get-connection (ds))]
|
||||||
|
(with-open [ps (jdbc/prepare
|
||||||
|
con
|
||||||
|
["select * from fruit where id = ?"])]
|
||||||
|
(jdbc/execute! (prep/set-parameters ps [4]))))]
|
||||||
|
(is (every? map? rs))
|
||||||
|
(is (every? meta rs))
|
||||||
|
(is (= 1 (count rs)))
|
||||||
|
(is (= 4 (:FRUIT/ID (first rs))))))
|
||||||
|
(testing "transact"
|
||||||
|
(is (= [{:next.jdbc/update-count 1}]
|
||||||
|
(jdbc/transact (ds)
|
||||||
|
(fn [t] (jdbc/execute! t ["
|
||||||
|
INSERT INTO fruit (id, name, appearance, cost, grade)
|
||||||
|
VALUES (5, 'Pear', 'green', 49, 47)
|
||||||
|
"]))
|
||||||
|
{:rollback-only true})))
|
||||||
|
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"])))))
|
||||||
|
(testing "with-transaction"
|
||||||
|
(is (= [{:next.jdbc/update-count 1}]
|
||||||
|
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
||||||
|
(jdbc/execute! t ["
|
||||||
|
INSERT INTO fruit (id, name, appearance, cost, grade)
|
||||||
|
VALUES (5, 'Pear', 'green', 49, 47)
|
||||||
|
"]))))
|
||||||
|
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"]))))))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue