Add optional MS SQL Server testing

This commit is contained in:
Sean Corfield 2019-11-15 22:37:42 -08:00
parent 774e08236f
commit 5b795f95a7
9 changed files with 156 additions and 89 deletions

1
.gitignore vendored
View file

@ -14,3 +14,4 @@ pom.xml.asc
/clojure_test*
/example*db
/derby.log
/run-tests.sh

View file

@ -6,6 +6,7 @@ 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:
* Add testing against Microsoft SQL Server (run tests with environment variables `NEXT_JDBC_TEST_MSSQL=yes` and `MSSQL_SA_PASSWORD` set to your local SQL Server `sa` user password; assumes that it can create and drop `fruit` and `fruit_time` tables in the `model` database).
* 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.

View file

@ -9,7 +9,8 @@
(:require [clojure.test :refer [deftest is testing use-fixtures]]
[next.jdbc :as jdbc]
[next.jdbc.date-time] ; to extend SettableParameter to date/time
[next.jdbc.test-fixtures :refer [with-test-db db ds]]
[next.jdbc.test-fixtures :refer [with-test-db db ds
mssql?]]
[next.jdbc.specs :as specs])
(:import (java.sql ResultSet)))
@ -21,26 +22,28 @@
(deftest issue-73
(try
(jdbc/execute-one! (ds) ["drop table temp_table"])
(jdbc/execute-one! (ds) ["drop table fruit_time"])
(catch Throwable _))
(jdbc/execute-one! (ds) ["create table temp_table (id int not null, deadline timestamp not null)"])
(jdbc/execute-one! (ds) ["insert into temp_table (id, deadline) values (?,?)" 1 (java.util.Date.)])
(jdbc/execute-one! (ds) ["insert into temp_table (id, deadline) values (?,?)" 2 (java.time.Instant/now)])
(jdbc/execute-one! (ds) ["insert into temp_table (id, deadline) values (?,?)" 3 (java.time.LocalDate/now)])
(jdbc/execute-one! (ds) ["insert into temp_table (id, deadline) values (?,?)" 4 (java.time.LocalDateTime/now)])
(jdbc/execute-one! (ds) [(str "create table fruit_time (id int not null, deadline "
(if (mssql?) "datetime" "timestamp")
" not null)")])
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 1 (java.util.Date.)])
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 2 (java.time.Instant/now)])
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 3 (java.time.LocalDate/now)])
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 4 (java.time.LocalDateTime/now)])
(try
(jdbc/execute-one! (ds) ["drop table temp_table"])
(jdbc/execute-one! (ds) ["drop table fruit_time"])
(catch Throwable _))
(jdbc/execute-one! (ds) ["create table temp_table (id int not null, deadline time not null)"])
(jdbc/execute-one! (ds) ["insert into temp_table (id, deadline) values (?,?)" 1 (java.util.Date.)])
(jdbc/execute-one! (ds) ["insert into temp_table (id, deadline) values (?,?)" 2 (java.time.Instant/now)])
(jdbc/execute-one! (ds) ["insert into temp_table (id, deadline) values (?,?)" 3 (java.time.LocalDate/now)])
(jdbc/execute-one! (ds) ["insert into temp_table (id, deadline) values (?,?)" 4 (java.time.LocalDateTime/now)])
(jdbc/execute-one! (ds) ["create table fruit_time (id int not null, deadline time not null)"])
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 1 (java.util.Date.)])
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 2 (java.time.Instant/now)])
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 3 (java.time.LocalDate/now)])
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 4 (java.time.LocalDateTime/now)])
(try
(jdbc/execute-one! (ds) ["drop table temp_table"])
(jdbc/execute-one! (ds) ["drop table fruit_time"])
(catch Throwable _))
(jdbc/execute-one! (ds) ["create table temp_table (id int not null, deadline date not null)"])
(jdbc/execute-one! (ds) ["insert into temp_table (id, deadline) values (?,?)" 1 (java.util.Date.)])
(jdbc/execute-one! (ds) ["insert into temp_table (id, deadline) values (?,?)" 2 (java.time.Instant/now)])
(jdbc/execute-one! (ds) ["insert into temp_table (id, deadline) values (?,?)" 3 (java.time.LocalDate/now)])
(jdbc/execute-one! (ds) ["insert into temp_table (id, deadline) values (?,?)" 4 (java.time.LocalDateTime/now)]))
(jdbc/execute-one! (ds) ["create table fruit_time (id int not null, deadline date not null)"])
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 1 (java.util.Date.)])
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 2 (java.time.Instant/now)])
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 3 (java.time.LocalDate/now)])
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 4 (java.time.LocalDateTime/now)]))

View file

@ -6,6 +6,7 @@
[next.jdbc.connection :as c]
[next.jdbc.middleware :as mw]
[next.jdbc.test-fixtures :refer [with-test-db db ds
default-options
derby? postgres?]]
[next.jdbc.prepare :as prep]
[next.jdbc.result-set :as rs]
@ -25,10 +26,11 @@
sql-p ["select * from fruit where id in (?,?) order by id desc" 1 4]]
(jdbc/execute! (mw/wrapper (ds))
sql-p
{:builder-fn rs/as-lower-maps
:sql-params-fn logger
:row!-fn logger
:rs!-fn logger})
(assoc (default-options)
:builder-fn rs/as-lower-maps
:sql-params-fn logger
:row!-fn logger
:rs!-fn logger))
;; should log four things
(is (= 4 (-> @logging count)))
;; :next.jdbc/sql-params value
@ -46,7 +48,8 @@
{:builder-fn rs/as-lower-maps
:sql-params-fn logger
:rs!-fn logger})
sql-p)
sql-p
(default-options))
;; should log two things
(is (= 2 (-> @logging count)))
;; :next.jdbc/sql-params value

View file

@ -6,7 +6,8 @@
[clojure.test :refer [deftest is testing use-fixtures]]
[next.jdbc.optional :as opt]
[next.jdbc.protocols :as p]
[next.jdbc.test-fixtures :refer [with-test-db ds column]])
[next.jdbc.test-fixtures :refer [with-test-db ds column
default-options]])
(:import (java.sql ResultSet ResultSetMetaData)))
(set! *warn-on-reflection* true)
@ -17,7 +18,8 @@
(testing "default row builder"
(let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 1]
{:builder-fn opt/as-maps})]
(assoc (default-options)
:builder-fn opt/as-maps))]
(is (map? row))
(is (not (contains? row (column :FRUIT/GRADE))))
(is (= 1 ((column :FRUIT/ID) row)))
@ -33,7 +35,8 @@
(testing "lower-case row builder"
(let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 3]
{:builder-fn opt/as-lower-maps})]
(assoc (default-options)
:builder-fn opt/as-lower-maps))]
(is (map? row))
(is (not (contains? row :fruit/appearance)))
(is (= 3 (:fruit/id row)))
@ -48,9 +51,10 @@
(testing "custom row builder"
(let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 3]
{:builder-fn opt/as-modified-maps
:label-fn str/lower-case
:qualifier-fn identity})]
(assoc (default-options)
:builder-fn opt/as-modified-maps
:label-fn str/lower-case
:qualifier-fn identity))]
(is (map? row))
(is (not (contains? row (column :FRUIT/appearance))))
(is (= 3 ((column :FRUIT/id) row)))
@ -64,9 +68,10 @@
(testing "default row builder"
(let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 1]
{:builder-fn (opt/as-maps-adapter
opt/as-maps
default-column-reader)})]
(assoc (default-options)
:builder-fn (opt/as-maps-adapter
opt/as-maps
default-column-reader)))]
(is (map? row))
(is (not (contains? row (column :FRUIT/GRADE))))
(is (= 1 ((column :FRUIT/ID) row)))
@ -84,9 +89,10 @@
(testing "lower-case row builder"
(let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 3]
{:builder-fn (opt/as-maps-adapter
opt/as-lower-maps
default-column-reader)})]
(assoc (default-options)
:builder-fn (opt/as-maps-adapter
opt/as-lower-maps
default-column-reader)))]
(is (map? row))
(is (not (contains? row :fruit/appearance)))
(is (= 3 (:fruit/id row)))
@ -103,11 +109,12 @@
(testing "custom row builder"
(let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 3]
{:builder-fn (opt/as-maps-adapter
opt/as-modified-maps
default-column-reader)
:label-fn str/lower-case
:qualifier-fn identity})]
(assoc (default-options)
:builder-fn (opt/as-maps-adapter
opt/as-modified-maps
default-column-reader)
:label-fn str/lower-case
:qualifier-fn identity))]
(is (map? row))
(is (not (contains? row (column :FRUIT/appearance))))
(is (= 3 ((column :FRUIT/id) row)))

View file

@ -12,7 +12,8 @@
[next.jdbc.protocols :as p]
[next.jdbc.result-set :as rs]
[next.jdbc.test-fixtures :refer [with-test-db ds column
mysql? postgres?]])
default-options
mssql? mysql? postgres?]])
(:import (java.sql ResultSet ResultSetMetaData)))
(set! *warn-on-reflection* true)
@ -22,7 +23,8 @@
(deftest test-datafy-nav
(testing "default schema"
(let [connectable (ds)
test-row (rs/datafiable-row {:TABLE/FRUIT_ID 1} connectable {})
test-row (rs/datafiable-row {:TABLE/FRUIT_ID 1} connectable
(default-options))
data (d/datafy test-row)
v (get data :TABLE/FRUIT_ID)]
;; check datafication is sane
@ -34,7 +36,8 @@
(testing "custom schema *-to-1"
(let [connectable (ds)
test-row (rs/datafiable-row {:foo/bar 2} connectable
{:schema {:foo/bar :fruit/id}})
(assoc (default-options)
:schema {:foo/bar :fruit/id}))
data (d/datafy test-row)
v (get data :foo/bar)]
;; check datafication is sane
@ -46,7 +49,8 @@
(testing "custom schema *-to-many"
(let [connectable (ds)
test-row (rs/datafiable-row {:foo/bar 3} connectable
{:schema {:foo/bar [:fruit/id]}})
(assoc (default-options)
:schema {:foo/bar [:fruit/id]}))
data (d/datafy test-row)
v (get data :foo/bar)]
;; check datafication is sane
@ -59,7 +63,8 @@
(testing "legacy schema tuples"
(let [connectable (ds)
test-row (rs/datafiable-row {:foo/bar 2} connectable
{:schema {:foo/bar [:fruit :id]}})
(assoc (default-options)
:schema {:foo/bar [:fruit :id]}))
data (d/datafy test-row)
v (get data :foo/bar)]
;; check datafication is sane
@ -70,7 +75,8 @@
(is (= "Banana" ((column :FRUIT/NAME) object)))))
(let [connectable (ds)
test-row (rs/datafiable-row {:foo/bar 3} connectable
{:schema {:foo/bar [:fruit :id :many]}})
(assoc (default-options)
:schema {:foo/bar [:fruit :id :many]}))
data (d/datafy test-row)
v (get data :foo/bar)]
;; check datafication is sane
@ -85,7 +91,7 @@
(testing "default row builder"
(let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 1]
{})]
(default-options))]
(is (map? row))
(is (contains? row (column :FRUIT/GRADE)))
(is (nil? ((column :FRUIT/GRADE) row)))
@ -93,7 +99,7 @@
(is (= "Apple" ((column :FRUIT/NAME) row))))
(let [rs (p/-execute-all (ds)
["select * from fruit order by id"]
{})]
(default-options))]
(is (every? map? rs))
(is (= 1 ((column :FRUIT/ID) (first rs))))
(is (= "Apple" ((column :FRUIT/NAME) (first rs))))
@ -111,7 +117,8 @@
(testing "lower-case row builder"
(let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 3]
{:builder-fn rs/as-lower-maps})]
(assoc (default-options)
:builder-fn rs/as-lower-maps))]
(is (map? row))
(is (contains? row :fruit/appearance))
(is (nil? (:fruit/appearance row)))
@ -127,9 +134,10 @@
(testing "custom row builder"
(let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 3]
{:builder-fn rs/as-modified-maps
:label-fn str/lower-case
:qualifier-fn identity})]
(assoc (default-options)
:builder-fn rs/as-modified-maps
:label-fn str/lower-case
:qualifier-fn identity))]
(is (map? row))
(is (contains? row (column :FRUIT/appearance)))
(is (nil? ((column :FRUIT/appearance) row)))
@ -138,7 +146,9 @@
(testing "adapted row builder"
(let [row (p/-execute-one (ds)
["select * from fruit where id = ?" 3]
{:builder-fn (rs/as-maps-adapter
(assoc
(default-options)
:builder-fn (rs/as-maps-adapter
rs/as-modified-maps
(fn [^ResultSet rs
^ResultSetMetaData rsmeta
@ -150,7 +160,7 @@
(.getLong rs i)
(.getObject rs i))))
:label-fn str/lower-case
:qualifier-fn identity})]
:qualifier-fn identity))]
(is (map? row))
(is (contains? row (column :FRUIT/appearance)))
(is (nil? ((column :FRUIT/appearance) row)))
@ -205,11 +215,13 @@
(is (map? (reduce (fn [_ row] (reduced
(dissoc row (column :FRUIT/NAME))))
nil
(p/-execute (ds) ["select * from fruit"] {}))))
(p/-execute (ds) ["select * from fruit"]
(default-options)))))
(is (= 4 (count (reduce (fn [_ row] (reduced
(dissoc row (column :FRUIT/NAME))))
nil
(p/-execute (ds) ["select * from fruit"] {})))))
(p/-execute (ds) ["select * from fruit"]
(default-options))))))
(is (seq? (reduce (fn [_ row] (reduced (seq row)))
nil
(p/-execute (ds) ["select * from fruit"] {}))))
@ -286,7 +298,7 @@
metadata))))
(deftest clob-reading
(when-not (or (mysql?) (postgres?)) ; no clob in mysql or embedded postgres
(when-not (or (mssql?) (mysql?) (postgres?)) ; no clob in these
(with-open [con (p/get-connection (ds) {})]
(try
(p/-execute-one con ["DROP TABLE CLOBBER"] {})

View file

@ -9,7 +9,8 @@
[next.jdbc.specs :as specs]
[next.jdbc.sql :as sql]
[next.jdbc.test-fixtures
:refer [with-test-db ds column derby? mysql? postgres? sqlite?]]))
:refer [with-test-db ds column default-options
derby? mssql? mysql? postgres? sqlite?]]))
(set! *warn-on-reflection* true)
@ -78,7 +79,8 @@
["INSERT INTO [user] (`id`, `status`) VALUES (?, ?), (?, ?), (?, ?)" 42 "hello" 35 "world" 64 "dollars"]))))
(deftest test-query
(let [rs (sql/query (ds) ["select * from fruit order by id"])]
(let [rs (sql/query (ds) ["select * from fruit order by id"]
(default-options))]
(is (= 4 (count rs)))
(is (every? map? rs))
(is (every? meta rs))
@ -89,7 +91,8 @@
(let [rs (sql/find-by-keys (ds) :fruit {:appearance "neon-green"})]
(is (vector? rs))
(is (= [] rs)))
(let [rs (sql/find-by-keys (ds) :fruit {:appearance "yellow"})]
(let [rs (sql/find-by-keys (ds) :fruit {:appearance "yellow"}
(default-options))]
(is (= 1 (count rs)))
(is (every? map? rs))
(is (every? meta rs))
@ -97,14 +100,14 @@
(deftest test-get-by-id
(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 (default-options))]
(is (map? 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 (default-options))]
(is (map? row))
(is (= 4 ((column :FRUIT/ID) 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 (default-options))]
(is (map? row))
(is (= 2 ((column :FRUIT/ID) row)))))
@ -113,7 +116,7 @@
(is (= {:next.jdbc/update-count 1}
(sql/update! (ds) :fruit {:appearance "brown"} {:id 2})))
(is (= "brown" ((column :FRUIT/APPEARANCE)
(sql/get-by-id (ds) :fruit 2))))
(sql/get-by-id (ds) :fruit 2 (default-options)))))
(finally
(sql/update! (ds) :fruit {:appearance "yellow"} {:id 2})))
(try
@ -121,21 +124,21 @@
(sql/update! (ds) :fruit {:appearance "green"}
["name = ?" "Banana"])))
(is (= "green" ((column :FRUIT/APPEARANCE)
(sql/get-by-id (ds) :fruit 2))))
(sql/get-by-id (ds) :fruit 2 (default-options)))))
(finally
(sql/update! (ds) :fruit {:appearance "yellow"} {:id 2}))))
(deftest test-insert-delete
(let [new-key (cond (derby?) :1
(mssql?) :GENERATED_KEYS
(mysql?) :GENERATED_KEY
(postgres?) :fruit/id
(sqlite?) (keyword "last_insert_rowid()")
:else :FRUIT/ID)]
(testing "single insert/delete"
(is (= (if (derby?) 5M 5)
(new-key (sql/insert! (ds) :fruit
{:name "Kiwi" :appearance "green & fuzzy"
:cost 100 :grade 99.9}))))
(is (== 5 (new-key (sql/insert! (ds) :fruit
{:name "Kiwi" :appearance "green & fuzzy"
:cost 100 :grade 99.9}))))
(is (= 5 (count (sql/query (ds) ["select * from fruit"]))))
(is (= {:next.jdbc/update-count 1}
(sql/delete! (ds) :fruit {:id 5})))
@ -143,6 +146,8 @@
(testing "multiple insert/delete"
(is (= (cond (derby?)
[nil] ; WTF Apache Derby?
(mssql?)
[8M]
(sqlite?)
[8]
:else
@ -163,6 +168,8 @@
(testing "multiple insert/delete with sequential cols/rows" ; per #43
(is (= (cond (derby?)
[nil] ; WTF Apache Derby?
(mssql?)
[11M]
(sqlite?)
[11]
:else

View file

@ -30,14 +30,22 @@
{:dbtype "mysql" :dbname "clojure_test" :useSSL false
:user "root" :password (System/getenv "MYSQL_ROOT_PASSWORD")}))
(def ^:private test-mssql
(when (System/getenv "NEXT_JDBC_TEST_MSSQL")
{:dbtype "mssql" :dbname "model"
:user "sa" :password (System/getenv "MSSQL_SA_PASSWORD")}))
(def ^:private test-db-specs
(cond-> [test-derby test-h2-mem test-h2 test-hsql test-sqlite test-postgres]
test-mysql (conj test-mysql)))
test-mysql (conj test-mysql)
test-mssql (conj test-mssql)))
(def ^:private test-db-spec (atom nil))
(defn derby? [] (= "derby" (:dbtype @test-db-spec)))
(defn mssql? [] (= "mssql" (:dbtype @test-db-spec)))
(defn mysql? [] (= "mysql" (:dbtype @test-db-spec)))
(defn postgres? [] (= "embedded-postgres" (:dbtype @test-db-spec)))
@ -47,11 +55,17 @@
(defn column [k]
(let [n (namespace k)]
(keyword (when n (cond (postgres?) (str/lower-case n)
(mssql?) (str/lower-case n)
(mysql?) (str/lower-case n)
:else n))
(cond (postgres?) (str/lower-case (name k))
:else (name k)))))
(defn default-options []
(if (mssql?) ; so that we get table names back from queries
{:result-type :scroll-insensitive :concurrency :read-only}
{}))
(def ^:private test-datasource (atom nil))
(defn db
@ -86,6 +100,8 @@
(postgres?)
(str "GENERATED ALWAYS AS IDENTITY"
" PRIMARY KEY")
(mssql?)
"IDENTITY PRIMARY KEY"
(sqlite?)
"PRIMARY KEY AUTOINCREMENT"
:else

View file

@ -7,7 +7,8 @@
[next.jdbc :as jdbc]
[next.jdbc.connection :as c]
[next.jdbc.test-fixtures :refer [with-test-db db ds column
derby? mysql? postgres?]]
default-options
derby? mssql? mysql? postgres?]]
[next.jdbc.prepare :as prep]
[next.jdbc.result-set :as rs]
[next.jdbc.specs :as specs])
@ -34,7 +35,8 @@
(is (= "Apple" ((column :FRUIT/NAME)
(jdbc/execute-one!
(ds)
["select * from fruit where appearance = ?" "red"])))))
["select * from fruit where appearance = ?" "red"]
(default-options))))))
(testing "execute!"
(let [rs (jdbc/execute!
(ds)
@ -43,13 +45,14 @@
(is (= [] rs)))
(let [rs (jdbc/execute!
(ds)
["select * from fruit where appearance = ?" "red"])]
["select * from fruit where appearance = ?" "red"]
(default-options))]
(is (= 1 (count rs)))
(is (= 1 ((column :FRUIT/ID) (first rs)))))
(let [rs (jdbc/execute!
(ds)
["select * from fruit order by id"]
{:builder-fn rs/as-maps})]
(assoc (default-options) :builder-fn rs/as-maps))]
(is (every? map? rs))
(is (every? meta rs))
(is (= 4 (count rs)))
@ -58,7 +61,7 @@
(let [rs (jdbc/execute!
(ds)
["select * from fruit order by id"]
{:builder-fn rs/as-arrays})]
(assoc (default-options) :builder-fn rs/as-arrays))]
(is (every? vector? rs))
(is (= 5 (count rs)))
(is (every? #(= 5 (count %)) rs))
@ -72,10 +75,11 @@
(let [rs (jdbc/execute! ; test again, with adapter and lower columns
(ds)
["select * from fruit order by id"]
{:builder-fn (rs/as-arrays-adapter
rs/as-lower-arrays
(fn [^ResultSet rs _ ^Integer i]
(.getObject rs i)))})]
(assoc (default-options)
:builder-fn (rs/as-arrays-adapter
rs/as-lower-arrays
(fn [^ResultSet rs _ ^Integer i]
(.getObject rs i)))))]
(is (every? vector? rs))
(is (= 5 (count rs)))
(is (every? #(= 5 (count %)) rs))
@ -113,7 +117,8 @@
(let [rs (with-open [con (jdbc/get-connection (ds))
ps (jdbc/prepare
con
["select * from fruit order by id"])]
["select * from fruit order by id"]
(default-options))]
(jdbc/execute! ps))]
(is (every? map? rs))
(is (every? meta rs))
@ -123,7 +128,8 @@
(let [rs (with-open [con (jdbc/get-connection (ds))
ps (jdbc/prepare
con
["select * from fruit where id = ?"])]
["select * from fruit where id = ?"]
(default-options))]
(jdbc/execute! (prep/set-parameters ps [4]) nil {}))]
(is (every? map? rs))
(is (every? meta rs))
@ -136,15 +142,19 @@
(is (every? map? rs))
(is (every? meta rs))
(is (= 4 (count rs)))
(is (= 1 ((column :FRUIT/ID) (first rs))))
(is (= 4 ((column :FRUIT/ID) (last rs)))))
;; SQL Server only returns table name if result-type/concurrency
;; provided, which we can only for a PreparedStatement
(is (= 1 ((if (mssql?) :ID (column :FRUIT/ID)) (first rs))))
(is (= 4 ((if (mssql?) :ID (column :FRUIT/ID)) (last rs)))))
(let [rs (with-open [con (jdbc/get-connection (ds))]
(jdbc/execute! (.createStatement con)
["select * from fruit where id = 4"]))]
(is (every? map? rs))
(is (every? meta rs))
(is (= 1 (count rs)))
(is (= 4 ((column :FRUIT/ID) (first rs))))))
;; SQL Server only returns table name if result-type/concurrency
;; provided, which we can only for a PreparedStatement
(is (= 4 ((if (mssql?) :ID (column :FRUIT/ID)) (first rs))))))
(testing "transact"
(is (= [{:next.jdbc/update-count 1}]
(jdbc/transact (ds)
@ -210,10 +220,14 @@ VALUES ('Pear', 'green', 49, 47)
(let [[url etc] (#'c/spec->url+etc (db))
ds (jdbc/get-datasource (assoc etc :jdbcUrl url))]
(cond (derby?) (is (= {:create true} etc))
(mssql?) (is (= #{:user :password} (set (keys etc))))
(mysql?) (is (= #{:user :password :useSSL} (set (keys etc))))
:else (is (= {} etc)))
(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:"
(if (mssql?)
"sqlserver" ; mssql is the alias
(:dbtype (db))))))
;; checks get-datasource on a DataSource is identity
(is (identical? ds (jdbc/get-datasource ds)))
(with-open [con (jdbc/get-connection ds {})]
@ -227,12 +241,15 @@ VALUES ('Pear', 'green', 49, 47)
;; this may succeed or not, depending on how the driver handles things
;; most drivers will error because the ResultSet was closed before pr-str
;; is invoked (which will attempt to print each row)
(let [s (pr-str (into [] (take 3) (jdbc/plan (ds) ["select * from fruit"])))]
(let [s (pr-str (into [] (take 3) (jdbc/plan (ds) ["select * from fruit"]
(default-options))))]
(is (or (re-find #"missing `map` or `reduce`" s)
(re-find #"(?i)^\[#:fruit\{.*:id.*\}\]$" s))))
(is (every? #(re-find #"(?i)^#:fruit\{.*:id.*\}$" %)
(into [] (map str) (jdbc/plan (ds) ["select * from fruit"]))))
(into [] (map str) (jdbc/plan (ds) ["select * from fruit"]
(default-options)))))
(is (every? #(re-find #"(?i)^#:fruit\{.*:id.*\}$" %)
(into [] (map pr-str) (jdbc/plan (ds) ["select * from fruit"]))))
(into [] (map pr-str) (jdbc/plan (ds) ["select * from fruit"]
(default-options)))))
(is (thrown? IllegalArgumentException
(doall (take 3 (jdbc/plan (ds) ["select * from fruit"]))))))