diff --git a/.gitignore b/.gitignore index b4e10e2..a50fd65 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ pom.xml.asc /clojure_test* /example*db /derby.log +/run-tests.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 31502e9..ca7df47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/test/next/jdbc/date_time_test.clj b/test/next/jdbc/date_time_test.clj index 2466a7c..581559d 100644 --- a/test/next/jdbc/date_time_test.clj +++ b/test/next/jdbc/date_time_test.clj @@ -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)])) diff --git a/test/next/jdbc/middleware_test.clj b/test/next/jdbc/middleware_test.clj index 0de1422..754e1c9 100644 --- a/test/next/jdbc/middleware_test.clj +++ b/test/next/jdbc/middleware_test.clj @@ -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 diff --git a/test/next/jdbc/optional_test.clj b/test/next/jdbc/optional_test.clj index f761206..09e0357 100644 --- a/test/next/jdbc/optional_test.clj +++ b/test/next/jdbc/optional_test.clj @@ -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))) diff --git a/test/next/jdbc/result_set_test.clj b/test/next/jdbc/result_set_test.clj index d2c7d19..48519e4 100644 --- a/test/next/jdbc/result_set_test.clj +++ b/test/next/jdbc/result_set_test.clj @@ -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"] {}) diff --git a/test/next/jdbc/sql_test.clj b/test/next/jdbc/sql_test.clj index 1b16a03..3dcb361 100644 --- a/test/next/jdbc/sql_test.clj +++ b/test/next/jdbc/sql_test.clj @@ -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 diff --git a/test/next/jdbc/test_fixtures.clj b/test/next/jdbc/test_fixtures.clj index f76a5dd..fde8fe5 100644 --- a/test/next/jdbc/test_fixtures.clj +++ b/test/next/jdbc/test_fixtures.clj @@ -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 diff --git a/test/next/jdbc_test.clj b/test/next/jdbc_test.clj index 635db64..ff63efc 100644 --- a/test/next/jdbc_test.clj +++ b/test/next/jdbc_test.clj @@ -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"]))))))