Add optional MySQL testing

This commit is contained in:
Sean Corfield 2019-11-15 15:38:51 -08:00
parent 8fbd0837ab
commit 774e08236f
7 changed files with 118 additions and 102 deletions

View file

@ -6,7 +6,9 @@ Only accretive/fixative changes will be made from now on.
The following changes have been committed to the **master** branch since the 1.0.10 release: The following changes have been committed to the **master** branch since the 1.0.10 release:
* None. * Add testing against MySQL (run tests with environment variables `NEXT_JDBC_TEST_MYSQL=yes` and `MYSQL_ROOT_PASSWORD` set to your local MySQL `root` user password; assumes you have already created an empty database called `clojure_test`).
* Bump several JDBC driver versions for up-to-date testing.
* Minor documentation fixes.
## Stable Builds ## Stable Builds

View file

@ -5,21 +5,21 @@
{:test {:extra-paths ["test"] {:test {:extra-paths ["test"]
:extra-deps {org.clojure/test.check {:mvn/version "0.10.0"} :extra-deps {org.clojure/test.check {:mvn/version "0.10.0"}
;; connection pooling ;; connection pooling
com.zaxxer/HikariCP {:mvn/version "3.3.1"} com.zaxxer/HikariCP {:mvn/version "3.4.1"}
com.mchange/c3p0 {:mvn/version "0.9.5.4"} com.mchange/c3p0 {:mvn/version "0.9.5.4"}
;; JDBC drivers ;; JDBC drivers
org.apache.derby/derby {:mvn/version "10.14.2.0"} ; behind org.apache.derby/derby {:mvn/version "10.14.2.0"} ; behind
org.hsqldb/hsqldb {:mvn/version "2.5.0"} org.hsqldb/hsqldb {:mvn/version "2.5.0"}
com.h2database/h2 {:mvn/version "1.4.199"} com.h2database/h2 {:mvn/version "1.4.200"}
net.sourceforge.jtds/jtds {:mvn/version "1.3.1"} net.sourceforge.jtds/jtds {:mvn/version "1.3.1"}
mysql/mysql-connector-java {:mvn/version "5.1.41"} ; behind mysql/mysql-connector-java {:mvn/version "8.0.18"}
org.postgresql/postgresql {:mvn/version "42.2.6"} org.postgresql/postgresql {:mvn/version "42.2.8"}
com.opentable.components/otj-pg-embedded {:mvn/version "0.13.1"} com.opentable.components/otj-pg-embedded {:mvn/version "0.13.3"}
com.impossibl.pgjdbc-ng/pgjdbc-ng {:mvn/version "0.8.2"} com.impossibl.pgjdbc-ng/pgjdbc-ng {:mvn/version "0.8.3"}
org.xerial/sqlite-jdbc {:mvn/version "3.28.0"} org.xerial/sqlite-jdbc {:mvn/version "3.28.0"}
com.microsoft.sqlserver/mssql-jdbc {:mvn/version "7.2.2.jre8"} com.microsoft.sqlserver/mssql-jdbc {:mvn/version "7.4.1.jre8"}
;; supplementary test stuff ;; supplementary test stuff
org.slf4j/slf4j-nop {:mvn/version "1.7.28"}}} org.slf4j/slf4j-nop {:mvn/version "1.7.29"}}}
:runner :runner
{:extra-deps {com.cognitect/test-runner {:extra-deps {com.cognitect/test-runner
{:git/url "https://github.com/cognitect-labs/test-runner" {:git/url "https://github.com/cognitect-labs/test-runner"

View file

@ -6,7 +6,7 @@
[clojure.test :refer [deftest is testing use-fixtures]] [clojure.test :refer [deftest is testing use-fixtures]]
[next.jdbc.optional :as opt] [next.jdbc.optional :as opt]
[next.jdbc.protocols :as p] [next.jdbc.protocols :as p]
[next.jdbc.test-fixtures :refer [with-test-db ds postgres?]]) [next.jdbc.test-fixtures :refer [with-test-db ds column]])
(:import (java.sql ResultSet ResultSetMetaData))) (:import (java.sql ResultSet ResultSetMetaData)))
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
@ -19,17 +19,17 @@
["select * from fruit where id = ?" 1] ["select * from fruit where id = ?" 1]
{:builder-fn opt/as-maps})] {:builder-fn opt/as-maps})]
(is (map? row)) (is (map? row))
(is (not (contains? row (if (postgres?) :fruit/grade :FRUIT/GRADE)))) (is (not (contains? row (column :FRUIT/GRADE))))
(is (= 1 ((if (postgres?) :fruit/id :FRUIT/ID) row))) (is (= 1 ((column :FRUIT/ID) row)))
(is (= "Apple" ((if (postgres?) :fruit/name :FRUIT/NAME) row))))) (is (= "Apple" ((column :FRUIT/NAME) row)))))
(testing "unqualified row builder" (testing "unqualified row builder"
(let [row (p/-execute-one (ds) (let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 2] ["select * from fruit where id = ?" 2]
{:builder-fn opt/as-unqualified-maps})] {:builder-fn opt/as-unqualified-maps})]
(is (map? row)) (is (map? row))
(is (not (contains? row (if (postgres?) :cost :COST)))) (is (not (contains? row (column :COST))))
(is (= 2 ((if (postgres?) :id :ID) row))) (is (= 2 ((column :ID) row)))
(is (= "Banana" ((if (postgres?) :name :NAME) row))))) (is (= "Banana" ((column :NAME) row)))))
(testing "lower-case row builder" (testing "lower-case row builder"
(let [row (p/-execute-one (ds) (let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 3] ["select * from fruit where id = ?" 3]
@ -52,9 +52,9 @@
:label-fn str/lower-case :label-fn str/lower-case
:qualifier-fn identity})] :qualifier-fn identity})]
(is (map? row)) (is (map? row))
(is (not (contains? row (if (postgres?) :fruit/appearance :FRUIT/appearance)))) (is (not (contains? row (column :FRUIT/appearance))))
(is (= 3 ((if (postgres?) :fruit/id :FRUIT/id) row))) (is (= 3 ((column :FRUIT/id) row)))
(is (= "Peach" ((if (postgres?) :fruit/name :FRUIT/name) row)))))) (is (= "Peach" ((column :FRUIT/name) row))))))
(defn- default-column-reader (defn- default-column-reader
[^ResultSet rs ^ResultSetMetaData rsmeta ^Integer i] [^ResultSet rs ^ResultSetMetaData rsmeta ^Integer i]
@ -68,9 +68,9 @@
opt/as-maps opt/as-maps
default-column-reader)})] default-column-reader)})]
(is (map? row)) (is (map? row))
(is (not (contains? row (if (postgres?) :fruit/grade :FRUIT/GRADE)))) (is (not (contains? row (column :FRUIT/GRADE))))
(is (= 1 ((if (postgres?) :fruit/id :FRUIT/ID) row))) (is (= 1 ((column :FRUIT/ID) row)))
(is (= "Apple" ((if (postgres?) :fruit/name :FRUIT/NAME) row))))) (is (= "Apple" ((column :FRUIT/NAME) row)))))
(testing "unqualified row builder" (testing "unqualified row builder"
(let [row (p/-execute-one (ds) (let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 2] ["select * from fruit where id = ?" 2]
@ -78,9 +78,9 @@
opt/as-unqualified-maps opt/as-unqualified-maps
default-column-reader)})] default-column-reader)})]
(is (map? row)) (is (map? row))
(is (not (contains? row (if (postgres?) :cost :COST)))) (is (not (contains? row (column :COST))))
(is (= 2 ((if (postgres?) :id :ID) row))) (is (= 2 ((column :ID) row)))
(is (= "Banana" ((if (postgres?) :name :NAME) row))))) (is (= "Banana" ((column :NAME) row)))))
(testing "lower-case row builder" (testing "lower-case row builder"
(let [row (p/-execute-one (ds) (let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 3] ["select * from fruit where id = ?" 3]
@ -109,6 +109,6 @@
:label-fn str/lower-case :label-fn str/lower-case
:qualifier-fn identity})] :qualifier-fn identity})]
(is (map? row)) (is (map? row))
(is (not (contains? row (if (postgres?) :fruit/appearance :FRUIT/appearance)))) (is (not (contains? row (column :FRUIT/appearance))))
(is (= 3 ((if (postgres?) :fruit/id :FRUIT/id) row))) (is (= 3 ((column :FRUIT/id) row)))
(is (= "Peach" ((if (postgres?) :fruit/name :FRUIT/name) row)))))) (is (= "Peach" ((column :FRUIT/name) row))))))

View file

@ -11,7 +11,8 @@
[clojure.test :refer [deftest is testing use-fixtures]] [clojure.test :refer [deftest is testing use-fixtures]]
[next.jdbc.protocols :as p] [next.jdbc.protocols :as p]
[next.jdbc.result-set :as rs] [next.jdbc.result-set :as rs]
[next.jdbc.test-fixtures :refer [with-test-db ds postgres?]]) [next.jdbc.test-fixtures :refer [with-test-db ds column
mysql? postgres?]])
(:import (java.sql ResultSet ResultSetMetaData))) (:import (java.sql ResultSet ResultSetMetaData)))
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
@ -28,8 +29,8 @@
(is (= 1 v)) (is (= 1 v))
(let [object (d/nav data :table/fruit_id v)] (let [object (d/nav data :table/fruit_id v)]
;; check nav produces a single map with the expected key/value data ;; check nav produces a single map with the expected key/value data
(is (= 1 ((if (postgres?) :fruit/id :FRUIT/ID) object))) (is (= 1 ((column :FRUIT/ID) object)))
(is (= "Apple" ((if (postgres?) :fruit/name :FRUIT/NAME) object)))))) (is (= "Apple" ((column :FRUIT/NAME) object))))))
(testing "custom schema *-to-1" (testing "custom schema *-to-1"
(let [connectable (ds) (let [connectable (ds)
test-row (rs/datafiable-row {:foo/bar 2} connectable test-row (rs/datafiable-row {:foo/bar 2} connectable
@ -40,8 +41,8 @@
(is (= 2 v)) (is (= 2 v))
(let [object (d/nav data :foo/bar v)] (let [object (d/nav data :foo/bar v)]
;; check nav produces a single map with the expected key/value data ;; check nav produces a single map with the expected key/value data
(is (= 2 ((if (postgres?) :fruit/id :FRUIT/ID) object))) (is (= 2 ((column :FRUIT/ID) object)))
(is (= "Banana" ((if (postgres?) :fruit/name :FRUIT/NAME) object)))))) (is (= "Banana" ((column :FRUIT/NAME) object))))))
(testing "custom schema *-to-many" (testing "custom schema *-to-many"
(let [connectable (ds) (let [connectable (ds)
test-row (rs/datafiable-row {:foo/bar 3} connectable test-row (rs/datafiable-row {:foo/bar 3} connectable
@ -53,8 +54,8 @@
(let [object (d/nav data :foo/bar v)] (let [object (d/nav data :foo/bar v)]
;; check nav produces a result set with the expected key/value data ;; check nav produces a result set with the expected key/value data
(is (vector? object)) (is (vector? object))
(is (= 3 ((if (postgres?) :fruit/id :FRUIT/ID) (first object)))) (is (= 3 ((column :FRUIT/ID) (first object))))
(is (= "Peach" ((if (postgres?) :fruit/name :FRUIT/NAME) (first object))))))) (is (= "Peach" ((column :FRUIT/NAME) (first object)))))))
(testing "legacy schema tuples" (testing "legacy schema tuples"
(let [connectable (ds) (let [connectable (ds)
test-row (rs/datafiable-row {:foo/bar 2} connectable test-row (rs/datafiable-row {:foo/bar 2} connectable
@ -65,8 +66,8 @@
(is (= 2 v)) (is (= 2 v))
(let [object (d/nav data :foo/bar v)] (let [object (d/nav data :foo/bar v)]
;; check nav produces a single map with the expected key/value data ;; check nav produces a single map with the expected key/value data
(is (= 2 ((if (postgres?) :fruit/id :FRUIT/ID) object))) (is (= 2 ((column :FRUIT/ID) object)))
(is (= "Banana" ((if (postgres?) :fruit/name :FRUIT/NAME) object))))) (is (= "Banana" ((column :FRUIT/NAME) object)))))
(let [connectable (ds) (let [connectable (ds)
test-row (rs/datafiable-row {:foo/bar 3} connectable test-row (rs/datafiable-row {:foo/bar 3} connectable
{:schema {:foo/bar [:fruit :id :many]}}) {:schema {:foo/bar [:fruit :id :many]}})
@ -77,8 +78,8 @@
(let [object (d/nav data :foo/bar v)] (let [object (d/nav data :foo/bar v)]
;; check nav produces a result set with the expected key/value data ;; check nav produces a result set with the expected key/value data
(is (vector? object)) (is (vector? object))
(is (= 3 ((if (postgres?) :fruit/id :FRUIT/ID) (first object)))) (is (= 3 ((column :FRUIT/ID) (first object))))
(is (= "Peach" ((if (postgres?) :fruit/name :FRUIT/NAME) (first object)))))))) (is (= "Peach" ((column :FRUIT/NAME) (first object))))))))
(deftest test-map-row-builder (deftest test-map-row-builder
(testing "default row builder" (testing "default row builder"
@ -86,27 +87,27 @@
["select * from fruit where id = ?" 1] ["select * from fruit where id = ?" 1]
{})] {})]
(is (map? row)) (is (map? row))
(is (contains? row (if (postgres?) :fruit/grade :FRUIT/GRADE))) (is (contains? row (column :FRUIT/GRADE)))
(is (nil? ((if (postgres?) :fruit/grade :FRUIT/GRADE) row))) (is (nil? ((column :FRUIT/GRADE) row)))
(is (= 1 ((if (postgres?) :fruit/id :FRUIT/ID) row))) (is (= 1 ((column :FRUIT/ID) row)))
(is (= "Apple" ((if (postgres?) :fruit/name :FRUIT/NAME) row)))) (is (= "Apple" ((column :FRUIT/NAME) row))))
(let [rs (p/-execute-all (ds) (let [rs (p/-execute-all (ds)
["select * from fruit order by id"] ["select * from fruit order by id"]
{})] {})]
(is (every? map? rs)) (is (every? map? rs))
(is (= 1 ((if (postgres?) :fruit/id :FRUIT/ID) (first rs)))) (is (= 1 ((column :FRUIT/ID) (first rs))))
(is (= "Apple" ((if (postgres?) :fruit/name :FRUIT/NAME) (first rs)))) (is (= "Apple" ((column :FRUIT/NAME) (first rs))))
(is (= 4 ((if (postgres?) :fruit/id :FRUIT/ID) (last rs)))) (is (= 4 ((column :FRUIT/ID) (last rs))))
(is (= "Orange" ((if (postgres?) :fruit/name :FRUIT/NAME) (last rs)))))) (is (= "Orange" ((column :FRUIT/NAME) (last rs))))))
(testing "unqualified row builder" (testing "unqualified row builder"
(let [row (p/-execute-one (ds) (let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 2] ["select * from fruit where id = ?" 2]
{:builder-fn rs/as-unqualified-maps})] {:builder-fn rs/as-unqualified-maps})]
(is (map? row)) (is (map? row))
(is (contains? row (if (postgres?) :cost :COST))) (is (contains? row (column :COST)))
(is (nil? ((if (postgres?) :cost :COST) row))) (is (nil? ((column :COST) row)))
(is (= 2 ((if (postgres?) :id :ID) row))) (is (= 2 ((column :ID) row)))
(is (= "Banana" ((if (postgres?) :name :NAME) row))))) (is (= "Banana" ((column :NAME) row)))))
(testing "lower-case row builder" (testing "lower-case row builder"
(let [row (p/-execute-one (ds) (let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 3] ["select * from fruit where id = ?" 3]
@ -130,10 +131,10 @@
:label-fn str/lower-case :label-fn str/lower-case
:qualifier-fn identity})] :qualifier-fn identity})]
(is (map? row)) (is (map? row))
(is (contains? row (if (postgres?) :fruit/appearance :FRUIT/appearance))) (is (contains? row (column :FRUIT/appearance)))
(is (nil? ((if (postgres?) :fruit/appearance :FRUIT/appearance) row))) (is (nil? ((column :FRUIT/appearance) row)))
(is (= 3 ((if (postgres?) :fruit/id :FRUIT/id) row))) (is (= 3 ((column :FRUIT/id) row)))
(is (= "Peach" ((if (postgres?) :fruit/name :FRUIT/name) row))))) (is (= "Peach" ((column :FRUIT/name) row)))))
(testing "adapted row builder" (testing "adapted row builder"
(let [row (p/-execute-one (ds) (let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 3] ["select * from fruit where id = ?" 3]
@ -151,10 +152,10 @@
:label-fn str/lower-case :label-fn str/lower-case
:qualifier-fn identity})] :qualifier-fn identity})]
(is (map? row)) (is (map? row))
(is (contains? row (if (postgres?) :fruit/appearance :FRUIT/appearance))) (is (contains? row (column :FRUIT/appearance)))
(is (nil? ((if (postgres?) :fruit/appearance :FRUIT/appearance) row))) (is (nil? ((column :FRUIT/appearance) row)))
(is (= 3 ((if (postgres?) :fruit/id :FRUIT/id) row))) (is (= 3 ((column :FRUIT/id) row)))
(is (= "Peach" ((if (postgres?) :fruit/name :FRUIT/name) row)))))) (is (= "Peach" ((column :FRUIT/name) row))))))
(deftest test-mapify (deftest test-mapify
(testing "no row builder is used" (testing "no row builder is used"
@ -202,17 +203,11 @@
nil nil
(p/-execute (ds) ["select * from fruit"] {}))))) (p/-execute (ds) ["select * from fruit"] {})))))
(is (map? (reduce (fn [_ row] (reduced (is (map? (reduce (fn [_ row] (reduced
(dissoc row (dissoc row (column :FRUIT/NAME))))
(if (postgres?)
:fruit/name
:FRUIT/NAME))))
nil nil
(p/-execute (ds) ["select * from fruit"] {})))) (p/-execute (ds) ["select * from fruit"] {}))))
(is (= 4 (count (reduce (fn [_ row] (reduced (is (= 4 (count (reduce (fn [_ row] (reduced
(dissoc row (dissoc row (column :FRUIT/NAME))))
(if (postgres?)
:fruit/name
:FRUIT/NAME))))
nil nil
(p/-execute (ds) ["select * from fruit"] {}))))) (p/-execute (ds) ["select * from fruit"] {})))))
(is (seq? (reduce (fn [_ row] (reduced (seq row))) (is (seq? (reduce (fn [_ row] (reduced (seq row)))
@ -291,7 +286,7 @@
metadata)))) metadata))))
(deftest clob-reading (deftest clob-reading
(when-not (postgres?) ; embedded postgres does not support clob (when-not (or (mysql?) (postgres?)) ; no clob in mysql or embedded postgres
(with-open [con (p/get-connection (ds) {})] (with-open [con (p/get-connection (ds) {})]
(try (try
(p/-execute-one con ["DROP TABLE CLOBBER"] {}) (p/-execute-one con ["DROP TABLE CLOBBER"] {})

View file

@ -9,7 +9,7 @@
[next.jdbc.specs :as specs] [next.jdbc.specs :as specs]
[next.jdbc.sql :as sql] [next.jdbc.sql :as sql]
[next.jdbc.test-fixtures [next.jdbc.test-fixtures
:refer [with-test-db ds derby? postgres? sqlite?]])) :refer [with-test-db ds column derby? mysql? postgres? sqlite?]]))
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
@ -82,8 +82,8 @@
(is (= 4 (count rs))) (is (= 4 (count rs)))
(is (every? map? rs)) (is (every? map? rs))
(is (every? meta rs)) (is (every? meta rs))
(is (= 1 ((if (postgres?) :fruit/id :FRUIT/ID) (first rs)))) (is (= 1 ((column :FRUIT/ID) (first rs))))
(is (= 4 ((if (postgres?) :fruit/id :FRUIT/ID) (last rs)))))) (is (= 4 ((column :FRUIT/ID) (last rs))))))
(deftest test-find-by-keys (deftest test-find-by-keys
(let [rs (sql/find-by-keys (ds) :fruit {:appearance "neon-green"})] (let [rs (sql/find-by-keys (ds) :fruit {:appearance "neon-green"})]
@ -93,26 +93,26 @@
(is (= 1 (count rs))) (is (= 1 (count rs)))
(is (every? map? rs)) (is (every? map? rs))
(is (every? meta rs)) (is (every? meta rs))
(is (= 2 ((if (postgres?) :fruit/id :FRUIT/ID) (first rs)))))) (is (= 2 ((column :FRUIT/ID) (first rs))))))
(deftest test-get-by-id (deftest test-get-by-id
(is (nil? (sql/get-by-id (ds) :fruit -1))) (is (nil? (sql/get-by-id (ds) :fruit -1)))
(let [row (sql/get-by-id (ds) :fruit 3)] (let [row (sql/get-by-id (ds) :fruit 3)]
(is (map? row)) (is (map? row))
(is (= "Peach" ((if (postgres?) :fruit/name :FRUIT/NAME) row)))) (is (= "Peach" ((column :FRUIT/NAME) row))))
(let [row (sql/get-by-id (ds) :fruit "juicy" :appearance {})] (let [row (sql/get-by-id (ds) :fruit "juicy" :appearance {})]
(is (map? row)) (is (map? row))
(is (= 4 ((if (postgres?) :fruit/id :FRUIT/ID) row))) (is (= 4 ((column :FRUIT/ID) row)))
(is (= "Orange" ((if (postgres?) :fruit/name :FRUIT/NAME) row)))) (is (= "Orange" ((column :FRUIT/NAME) row))))
(let [row (sql/get-by-id (ds) :fruit "Banana" :FRUIT/NAME {})] (let [row (sql/get-by-id (ds) :fruit "Banana" :FRUIT/NAME {})]
(is (map? row)) (is (map? row))
(is (= 2 ((if (postgres?) :fruit/id :FRUIT/ID) row))))) (is (= 2 ((column :FRUIT/ID) row)))))
(deftest test-update! (deftest test-update!
(try (try
(is (= {:next.jdbc/update-count 1} (is (= {:next.jdbc/update-count 1}
(sql/update! (ds) :fruit {:appearance "brown"} {:id 2}))) (sql/update! (ds) :fruit {:appearance "brown"} {:id 2})))
(is (= "brown" ((if (postgres?) :fruit/appearance :FRUIT/APPEARANCE) (is (= "brown" ((column :FRUIT/APPEARANCE)
(sql/get-by-id (ds) :fruit 2)))) (sql/get-by-id (ds) :fruit 2))))
(finally (finally
(sql/update! (ds) :fruit {:appearance "yellow"} {:id 2}))) (sql/update! (ds) :fruit {:appearance "yellow"} {:id 2})))
@ -120,13 +120,14 @@
(is (= {:next.jdbc/update-count 1} (is (= {:next.jdbc/update-count 1}
(sql/update! (ds) :fruit {:appearance "green"} (sql/update! (ds) :fruit {:appearance "green"}
["name = ?" "Banana"]))) ["name = ?" "Banana"])))
(is (= "green" ((if (postgres?) :fruit/appearance :FRUIT/APPEARANCE) (is (= "green" ((column :FRUIT/APPEARANCE)
(sql/get-by-id (ds) :fruit 2)))) (sql/get-by-id (ds) :fruit 2))))
(finally (finally
(sql/update! (ds) :fruit {:appearance "yellow"} {:id 2})))) (sql/update! (ds) :fruit {:appearance "yellow"} {:id 2}))))
(deftest test-insert-delete (deftest test-insert-delete
(let [new-key (cond (derby?) :1 (let [new-key (cond (derby?) :1
(mysql?) :GENERATED_KEY
(postgres?) :fruit/id (postgres?) :fruit/id
(sqlite?) (keyword "last_insert_rowid()") (sqlite?) (keyword "last_insert_rowid()")
:else :FRUIT/ID)] :else :FRUIT/ID)]

View file

@ -2,7 +2,8 @@
(ns next.jdbc.test-fixtures (ns next.jdbc.test-fixtures
"Multi-database testing fixtures." "Multi-database testing fixtures."
(:require [next.jdbc :as jdbc] (:require [clojure.string :as str]
[next.jdbc :as jdbc]
[next.jdbc.sql :as sql]) [next.jdbc.sql :as sql])
(:import (com.opentable.db.postgres.embedded EmbeddedPostgres) (:import (com.opentable.db.postgres.embedded EmbeddedPostgres)
(javax.sql DataSource))) (javax.sql DataSource)))
@ -24,17 +25,33 @@
;; it takes a while to spin up so we kick it off at startup ;; it takes a while to spin up so we kick it off at startup
(defonce embedded-pg (future (EmbeddedPostgres/start))) (defonce embedded-pg (future (EmbeddedPostgres/start)))
(def ^:private test-mysql
(when (System/getenv "NEXT_JDBC_TEST_MYSQL")
{:dbtype "mysql" :dbname "clojure_test" :useSSL false
:user "root" :password (System/getenv "MYSQL_ROOT_PASSWORD")}))
(def ^:private test-db-specs (def ^:private test-db-specs
[test-derby test-h2-mem test-h2 test-hsql test-sqlite test-postgres]) (cond-> [test-derby test-h2-mem test-h2 test-hsql test-sqlite test-postgres]
test-mysql (conj test-mysql)))
(def ^:private test-db-spec (atom nil)) (def ^:private test-db-spec (atom nil))
(defn derby? [] (= "derby" (:dbtype @test-db-spec))) (defn derby? [] (= "derby" (:dbtype @test-db-spec)))
(defn mysql? [] (= "mysql" (:dbtype @test-db-spec)))
(defn postgres? [] (= "embedded-postgres" (:dbtype @test-db-spec))) (defn postgres? [] (= "embedded-postgres" (:dbtype @test-db-spec)))
(defn sqlite? [] (= "sqlite" (:dbtype @test-db-spec))) (defn sqlite? [] (= "sqlite" (:dbtype @test-db-spec)))
(defn column [k]
(let [n (namespace k)]
(keyword (when n (cond (postgres?) (str/lower-case n)
(mysql?) (str/lower-case n)
:else n))
(cond (postgres?) (str/lower-case (name k))
:else (name k)))))
(def ^:private test-datasource (atom nil)) (def ^:private test-datasource (atom nil))
(defn db (defn db
@ -60,7 +77,8 @@
(reset! test-datasource (reset! test-datasource
(.getPostgresDatabase ^EmbeddedPostgres @embedded-pg)) (.getPostgresDatabase ^EmbeddedPostgres @embedded-pg))
(reset! test-datasource (jdbc/get-datasource db))) (reset! test-datasource (jdbc/get-datasource db)))
(let [auto-inc-pk (let [fruit (if (mysql?) "fruit" "FRUIT") ; MySQL is case sensitive!
auto-inc-pk
(cond (or (derby?) (= "hsqldb" (:dbtype db))) (cond (or (derby?) (= "hsqldb" (:dbtype db)))
(str "GENERATED ALWAYS AS IDENTITY" (str "GENERATED ALWAYS AS IDENTITY"
" (START WITH 1, INCREMENT BY 1)" " (START WITH 1, INCREMENT BY 1)"
@ -74,10 +92,10 @@
"AUTO_INCREMENT PRIMARY KEY")] "AUTO_INCREMENT PRIMARY KEY")]
(with-open [con (jdbc/get-connection (ds))] (with-open [con (jdbc/get-connection (ds))]
(try (try
(jdbc/execute-one! con ["DROP TABLE FRUIT"]) (jdbc/execute-one! con [(str "DROP TABLE " fruit)])
(catch Exception _)) (catch Exception _))
(jdbc/execute-one! con [(str " (jdbc/execute-one! con [(str "
CREATE TABLE FRUIT ( CREATE TABLE " fruit " (
ID INTEGER " auto-inc-pk ", ID INTEGER " auto-inc-pk ",
NAME VARCHAR(32), NAME VARCHAR(32),
APPEARANCE VARCHAR(32) DEFAULT NULL, APPEARANCE VARCHAR(32) DEFAULT NULL,

View file

@ -6,8 +6,8 @@
[clojure.test :refer [deftest is testing use-fixtures]] [clojure.test :refer [deftest is testing use-fixtures]]
[next.jdbc :as jdbc] [next.jdbc :as jdbc]
[next.jdbc.connection :as c] [next.jdbc.connection :as c]
[next.jdbc.test-fixtures :refer [with-test-db db ds [next.jdbc.test-fixtures :refer [with-test-db db ds column
derby? postgres?]] derby? mysql? postgres?]]
[next.jdbc.prepare :as prep] [next.jdbc.prepare :as prep]
[next.jdbc.result-set :as rs] [next.jdbc.result-set :as rs]
[next.jdbc.specs :as specs]) [next.jdbc.specs :as specs])
@ -31,7 +31,7 @@
(is (nil? (jdbc/execute-one! (is (nil? (jdbc/execute-one!
(ds) (ds)
["select * from fruit where appearance = ?" "neon-green"]))) ["select * from fruit where appearance = ?" "neon-green"])))
(is (= "Apple" ((if (postgres?) :fruit/name :FRUIT/NAME) (is (= "Apple" ((column :FRUIT/NAME)
(jdbc/execute-one! (jdbc/execute-one!
(ds) (ds)
["select * from fruit where appearance = ?" "red"]))))) ["select * from fruit where appearance = ?" "red"])))))
@ -45,7 +45,7 @@
(ds) (ds)
["select * from fruit where appearance = ?" "red"])] ["select * from fruit where appearance = ?" "red"])]
(is (= 1 (count rs))) (is (= 1 (count rs)))
(is (= 1 ((if (postgres?) :fruit/id :FRUIT/ID) (first rs))))) (is (= 1 ((column :FRUIT/ID) (first rs)))))
(let [rs (jdbc/execute! (let [rs (jdbc/execute!
(ds) (ds)
["select * from fruit order by id"] ["select * from fruit order by id"]
@ -53,8 +53,8 @@
(is (every? map? rs)) (is (every? map? rs))
(is (every? meta rs)) (is (every? meta rs))
(is (= 4 (count rs))) (is (= 4 (count rs)))
(is (= 1 ((if (postgres?) :fruit/id :FRUIT/ID) (first rs)))) (is (= 1 ((column :FRUIT/ID) (first rs))))
(is (= 4 ((if (postgres?) :fruit/id :FRUIT/ID) (last rs))))) (is (= 4 ((column :FRUIT/ID) (last rs)))))
(let [rs (jdbc/execute! (let [rs (jdbc/execute!
(ds) (ds)
["select * from fruit order by id"] ["select * from fruit order by id"]
@ -65,7 +65,7 @@
;; columns come first ;; columns come first
(is (every? qualified-keyword? (first rs))) (is (every? qualified-keyword? (first rs)))
;; :FRUIT/ID should be first column ;; :FRUIT/ID should be first column
(is (= (if (postgres?) :fruit/id :FRUIT/ID) (ffirst rs))) (is (= (column :FRUIT/ID) (ffirst rs)))
;; and all its corresponding values should be ints ;; and all its corresponding values should be ints
(is (every? int? (map first (rest rs)))) (is (every? int? (map first (rest rs))))
(is (every? string? (map second (rest rs))))) (is (every? string? (map second (rest rs)))))
@ -93,8 +93,8 @@
(is (every? map? rs)) (is (every? map? rs))
(is (every? meta rs)) (is (every? meta rs))
(is (= 4 (count rs))) (is (= 4 (count rs)))
(is (= 1 ((if (postgres?) :id :ID) (first rs)))) (is (= 1 ((column :ID) (first rs))))
(is (= 4 ((if (postgres?) :id :ID) (last rs))))) (is (= 4 ((column :ID) (last rs)))))
(let [rs (jdbc/execute! (let [rs (jdbc/execute!
(ds) (ds)
["select * from fruit order by id"] ["select * from fruit order by id"]
@ -105,7 +105,7 @@
;; columns come first ;; columns come first
(is (every? simple-keyword? (first rs))) (is (every? simple-keyword? (first rs)))
;; :ID should be first column ;; :ID should be first column
(is (= (if (postgres?) :id :ID) (ffirst rs))) (is (= (column :ID) (ffirst rs)))
;; and all its corresponding values should be ints ;; and all its corresponding values should be ints
(is (every? int? (map first (rest rs)))) (is (every? int? (map first (rest rs))))
(is (every? string? (map second (rest rs)))))) (is (every? string? (map second (rest rs))))))
@ -118,8 +118,8 @@
(is (every? map? rs)) (is (every? map? rs))
(is (every? meta rs)) (is (every? meta rs))
(is (= 4 (count rs))) (is (= 4 (count rs)))
(is (= 1 ((if (postgres?) :fruit/id :FRUIT/ID) (first rs)))) (is (= 1 ((column :FRUIT/ID) (first rs))))
(is (= 4 ((if (postgres?) :fruit/id :FRUIT/ID) (last rs))))) (is (= 4 ((column :FRUIT/ID) (last rs)))))
(let [rs (with-open [con (jdbc/get-connection (ds)) (let [rs (with-open [con (jdbc/get-connection (ds))
ps (jdbc/prepare ps (jdbc/prepare
con con
@ -128,7 +128,7 @@
(is (every? map? rs)) (is (every? map? rs))
(is (every? meta rs)) (is (every? meta rs))
(is (= 1 (count rs))) (is (= 1 (count rs)))
(is (= 4 ((if (postgres?) :fruit/id :FRUIT/ID) (first rs)))))) (is (= 4 ((column :FRUIT/ID) (first rs))))))
(testing "statement" (testing "statement"
(let [rs (with-open [con (jdbc/get-connection (ds))] (let [rs (with-open [con (jdbc/get-connection (ds))]
(jdbc/execute! (.createStatement con) (jdbc/execute! (.createStatement con)
@ -136,15 +136,15 @@
(is (every? map? rs)) (is (every? map? rs))
(is (every? meta rs)) (is (every? meta rs))
(is (= 4 (count rs))) (is (= 4 (count rs)))
(is (= 1 ((if (postgres?) :fruit/id :FRUIT/ID) (first rs)))) (is (= 1 ((column :FRUIT/ID) (first rs))))
(is (= 4 ((if (postgres?) :fruit/id :FRUIT/ID) (last rs))))) (is (= 4 ((column :FRUIT/ID) (last rs)))))
(let [rs (with-open [con (jdbc/get-connection (ds))] (let [rs (with-open [con (jdbc/get-connection (ds))]
(jdbc/execute! (.createStatement con) (jdbc/execute! (.createStatement con)
["select * from fruit where id = 4"]))] ["select * from fruit where id = 4"]))]
(is (every? map? rs)) (is (every? map? rs))
(is (every? meta rs)) (is (every? meta rs))
(is (= 1 (count rs))) (is (= 1 (count rs)))
(is (= 4 ((if (postgres?) :fruit/id :FRUIT/ID) (first rs)))))) (is (= 4 ((column :FRUIT/ID) (first rs))))))
(testing "transact" (testing "transact"
(is (= [{:next.jdbc/update-count 1}] (is (= [{:next.jdbc/update-count 1}]
(jdbc/transact (ds) (jdbc/transact (ds)
@ -209,9 +209,9 @@ VALUES ('Pear', 'green', 49, 47)
(when-not (postgres?) (when-not (postgres?)
(let [[url etc] (#'c/spec->url+etc (db)) (let [[url etc] (#'c/spec->url+etc (db))
ds (jdbc/get-datasource (assoc etc :jdbcUrl url))] ds (jdbc/get-datasource (assoc etc :jdbcUrl url))]
(if (derby?) (cond (derby?) (is (= {:create true} etc))
(is (= {:create true} etc)) (mysql?) (is (= #{:user :password :useSSL} (set (keys etc))))
(is (= {} etc))) :else (is (= {} etc)))
(is (instance? javax.sql.DataSource ds)) (is (instance? javax.sql.DataSource ds))
(is (str/index-of (pr-str ds) (str "jdbc:" (:dbtype (db))))) (is (str/index-of (pr-str ds) (str "jdbc:" (:dbtype (db)))))
;; checks get-datasource on a DataSource is identity ;; checks get-datasource on a DataSource is identity