wip xtdb testing
Signed-off-by: Sean Corfield <sean@corfield.org>
This commit is contained in:
parent
ecd950d009
commit
0c50cf28b5
10 changed files with 770 additions and 637 deletions
8
.clj-kondo/com.github.seancorfield/next.jdbc/config.edn
Normal file
8
.clj-kondo/com.github.seancorfield/next.jdbc/config.edn
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{:hooks
|
||||||
|
{:analyze-call
|
||||||
|
{next.jdbc/with-transaction
|
||||||
|
hooks.com.github.seancorfield.next-jdbc/with-transaction
|
||||||
|
next.jdbc/with-transaction+options
|
||||||
|
hooks.com.github.seancorfield.next-jdbc/with-transaction+options}}
|
||||||
|
:lint-as {next.jdbc/on-connection clojure.core/with-open
|
||||||
|
next.jdbc/on-connection+options clojure.core/with-open}}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
(ns hooks.com.github.seancorfield.next-jdbc
|
||||||
|
(:require [clj-kondo.hooks-api :as api]))
|
||||||
|
|
||||||
|
(defn with-transaction
|
||||||
|
"Expands (with-transaction [tx expr opts] body)
|
||||||
|
to (let [tx expr] opts body) per clj-kondo examples."
|
||||||
|
[{:keys [:node]}]
|
||||||
|
(let [[binding-vec & body] (rest (:children node))
|
||||||
|
[sym val opts] (:children binding-vec)]
|
||||||
|
(when-not (and sym val)
|
||||||
|
(throw (ex-info "No sym and val provided" {})))
|
||||||
|
(let [new-node (api/list-node
|
||||||
|
(list*
|
||||||
|
(api/token-node 'let)
|
||||||
|
(api/vector-node [sym val])
|
||||||
|
opts
|
||||||
|
body))]
|
||||||
|
{:node new-node})))
|
||||||
|
|
||||||
|
(defn with-transaction+options
|
||||||
|
"Expands (with-transaction+options [tx expr opts] body)
|
||||||
|
to (let [tx expr] opts body) per clj-kondo examples."
|
||||||
|
[{:keys [:node]}]
|
||||||
|
(let [[binding-vec & body] (rest (:children node))
|
||||||
|
[sym val opts] (:children binding-vec)]
|
||||||
|
(when-not (and sym val)
|
||||||
|
(throw (ex-info "No sym and val provided" {})))
|
||||||
|
(let [new-node (api/list-node
|
||||||
|
(list*
|
||||||
|
(api/token-node 'let)
|
||||||
|
(api/vector-node [sym val])
|
||||||
|
opts
|
||||||
|
body))]
|
||||||
|
{:node new-node})))
|
||||||
5
deps.edn
5
deps.edn
|
|
@ -1,4 +1,5 @@
|
||||||
{:mvn/repos {"sonatype" {:url "https://oss.sonatype.org/content/repositories/snapshots/"}}
|
{:mvn/repos {"sonatype" {:url "https://oss.sonatype.org/content/repositories/snapshots/"}
|
||||||
|
"ossrh-snapshots" {:url "https://s01.oss.sonatype.org/content/repositories/snapshots"}}
|
||||||
:paths ["src" "resources"]
|
:paths ["src" "resources"]
|
||||||
:deps {org.clojure/clojure {:mvn/version "1.10.3"}
|
:deps {org.clojure/clojure {:mvn/version "1.10.3"}
|
||||||
org.clojure/java.data {:mvn/version "1.2.107"}
|
org.clojure/java.data {:mvn/version "1.2.107"}
|
||||||
|
|
@ -40,6 +41,8 @@
|
||||||
io.zonky.test.postgres/embedded-postgres-binaries-windows-amd64 {:mvn/version "17.0.0"}
|
io.zonky.test.postgres/embedded-postgres-binaries-windows-amd64 {:mvn/version "17.0.0"}
|
||||||
org.xerial/sqlite-jdbc {:mvn/version "3.46.1.3"}
|
org.xerial/sqlite-jdbc {:mvn/version "3.46.1.3"}
|
||||||
com.microsoft.sqlserver/mssql-jdbc {:mvn/version "12.8.1.jre11"}
|
com.microsoft.sqlserver/mssql-jdbc {:mvn/version "12.8.1.jre11"}
|
||||||
|
;; prerelease XTDB JDBC module:
|
||||||
|
com.xtdb/xtdb-jdbc {:mvn/version "2.0.0-SNAPSHOT"}
|
||||||
;; use log4j2 to reduce log noise during testing:
|
;; use log4j2 to reduce log noise during testing:
|
||||||
org.apache.logging.log4j/log4j-api {:mvn/version "2.24.0"}
|
org.apache.logging.log4j/log4j-api {:mvn/version "2.24.0"}
|
||||||
;; bridge everything into log4j:
|
;; bridge everything into log4j:
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,8 @@ services:
|
||||||
MSSQL_SA_PASSWORD: Str0ngP4ssw0rd
|
MSSQL_SA_PASSWORD: Str0ngP4ssw0rd
|
||||||
ports:
|
ports:
|
||||||
- "1433:1433"
|
- "1433:1433"
|
||||||
|
xtdb:
|
||||||
|
image: ghcr.io/xtdb/xtdb
|
||||||
|
pull_policy: always
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
[next.jdbc :as jdbc]
|
[next.jdbc :as jdbc]
|
||||||
[next.jdbc.date-time] ; to extend SettableParameter to date/time
|
[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?]]
|
mssql? xtdb?]]
|
||||||
[next.jdbc.specs :as specs])
|
[next.jdbc.specs :as specs])
|
||||||
(:import (java.sql ResultSet)))
|
(:import (java.sql ResultSet)))
|
||||||
|
|
||||||
|
|
@ -21,29 +21,30 @@
|
||||||
(specs/instrument)
|
(specs/instrument)
|
||||||
|
|
||||||
(deftest issue-73
|
(deftest issue-73
|
||||||
(try
|
(when-not (xtdb?)
|
||||||
(jdbc/execute-one! (ds) ["drop table fruit_time"])
|
(try
|
||||||
(catch Throwable _))
|
(jdbc/execute-one! (ds) ["drop table fruit_time"])
|
||||||
(jdbc/execute-one! (ds) [(str "create table fruit_time (id int not null, deadline "
|
(catch Throwable _))
|
||||||
(if (mssql?) "datetime" "timestamp")
|
(jdbc/execute-one! (ds) [(str "create table fruit_time (id int not null, deadline "
|
||||||
" not null)")])
|
(if (mssql?) "datetime" "timestamp")
|
||||||
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 1 (java.util.Date.)])
|
" not null)")])
|
||||||
(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 (?,?)" 1 (java.util.Date.)])
|
||||||
(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 (?,?)" 2 (java.time.Instant/now)])
|
||||||
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 4 (java.time.LocalDateTime/now)])
|
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 3 (java.time.LocalDate/now)])
|
||||||
(try
|
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 4 (java.time.LocalDateTime/now)])
|
||||||
(jdbc/execute-one! (ds) ["drop table fruit_time"])
|
(try
|
||||||
(catch Throwable _))
|
(jdbc/execute-one! (ds) ["drop table fruit_time"])
|
||||||
(jdbc/execute-one! (ds) ["create table fruit_time (id int not null, deadline time not null)"])
|
(catch Throwable _))
|
||||||
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 1 (java.util.Date.)])
|
(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 (?,?)" 2 (java.time.Instant/now)])
|
(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 (?,?)" 3 (java.time.LocalDate/now)])
|
(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 (?,?)" 4 (java.time.LocalDateTime/now)])
|
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 3 (java.time.LocalDate/now)])
|
||||||
(try
|
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 4 (java.time.LocalDateTime/now)])
|
||||||
(jdbc/execute-one! (ds) ["drop table fruit_time"])
|
(try
|
||||||
(catch Throwable _))
|
(jdbc/execute-one! (ds) ["drop table fruit_time"])
|
||||||
(jdbc/execute-one! (ds) ["create table fruit_time (id int not null, deadline date not null)"])
|
(catch Throwable _))
|
||||||
(jdbc/execute-one! (ds) ["insert into fruit_time (id, deadline) values (?,?)" 1 (java.util.Date.)])
|
(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 (?,?)" 2 (java.time.Instant/now)])
|
(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 (?,?)" 3 (java.time.LocalDate/now)])
|
(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 (?,?)" 4 (java.time.LocalDateTime/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)])))
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
(:require [clojure.test :refer [deftest is testing use-fixtures]]
|
(:require [clojure.test :refer [deftest is testing use-fixtures]]
|
||||||
[next.jdbc :as jdbc]
|
[next.jdbc :as jdbc]
|
||||||
[next.jdbc.test-fixtures
|
[next.jdbc.test-fixtures
|
||||||
:refer [with-test-db ds jtds? mssql? sqlite?]]
|
:refer [with-test-db ds jtds? mssql? sqlite? xtdb?]]
|
||||||
[next.jdbc.prepare :as prep]
|
[next.jdbc.prepare :as prep]
|
||||||
[next.jdbc.specs :as specs]))
|
[next.jdbc.specs :as specs]))
|
||||||
|
|
||||||
|
|
@ -22,61 +22,25 @@
|
||||||
(specs/instrument)
|
(specs/instrument)
|
||||||
|
|
||||||
(deftest execute-batch-tests
|
(deftest execute-batch-tests
|
||||||
(testing "simple batch insert"
|
(when-not (xtdb?)
|
||||||
(is (= [1 1 1 1 1 1 1 1 1 13]
|
(testing "simple batch insert"
|
||||||
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
(is (= [1 1 1 1 1 1 1 1 1 13]
|
||||||
(with-open [ps (jdbc/prepare t ["
|
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
||||||
|
(with-open [ps (jdbc/prepare t ["
|
||||||
INSERT INTO fruit (name, appearance) VALUES (?,?)
|
INSERT INTO fruit (name, appearance) VALUES (?,?)
|
||||||
"])]
|
"])]
|
||||||
(let [result (prep/execute-batch! ps [["fruit1" "one"]
|
(let [result (prep/execute-batch! ps [["fruit1" "one"]
|
||||||
["fruit2" "two"]
|
["fruit2" "two"]
|
||||||
["fruit3" "three"]
|
["fruit3" "three"]
|
||||||
["fruit4" "four"]
|
["fruit4" "four"]
|
||||||
["fruit5" "five"]
|
["fruit5" "five"]
|
||||||
["fruit6" "six"]
|
["fruit6" "six"]
|
||||||
["fruit7" "seven"]
|
["fruit7" "seven"]
|
||||||
["fruit8" "eight"]
|
["fruit8" "eight"]
|
||||||
["fruit9" "nine"]])]
|
["fruit9" "nine"]])]
|
||||||
(conj result (count (jdbc/execute! t ["select * from fruit"]))))))))
|
(conj result (count (jdbc/execute! t ["select * from fruit"]))))))))
|
||||||
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"])))))
|
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"])))))
|
||||||
(testing "small batch insert"
|
(testing "small batch insert"
|
||||||
(is (= [1 1 1 1 1 1 1 1 1 13]
|
|
||||||
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
|
||||||
(with-open [ps (jdbc/prepare t ["
|
|
||||||
INSERT INTO fruit (name, appearance) VALUES (?,?)
|
|
||||||
"])]
|
|
||||||
(let [result (prep/execute-batch! ps [["fruit1" "one"]
|
|
||||||
["fruit2" "two"]
|
|
||||||
["fruit3" "three"]
|
|
||||||
["fruit4" "four"]
|
|
||||||
["fruit5" "five"]
|
|
||||||
["fruit6" "six"]
|
|
||||||
["fruit7" "seven"]
|
|
||||||
["fruit8" "eight"]
|
|
||||||
["fruit9" "nine"]]
|
|
||||||
{:batch-size 3})]
|
|
||||||
(conj result (count (jdbc/execute! t ["select * from fruit"]))))))))
|
|
||||||
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"])))))
|
|
||||||
(testing "big batch insert"
|
|
||||||
(is (= [1 1 1 1 1 1 1 1 1 13]
|
|
||||||
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
|
||||||
(with-open [ps (jdbc/prepare t ["
|
|
||||||
INSERT INTO fruit (name, appearance) VALUES (?,?)
|
|
||||||
"])]
|
|
||||||
(let [result (prep/execute-batch! ps [["fruit1" "one"]
|
|
||||||
["fruit2" "two"]
|
|
||||||
["fruit3" "three"]
|
|
||||||
["fruit4" "four"]
|
|
||||||
["fruit5" "five"]
|
|
||||||
["fruit6" "six"]
|
|
||||||
["fruit7" "seven"]
|
|
||||||
["fruit8" "eight"]
|
|
||||||
["fruit9" "nine"]]
|
|
||||||
{:batch-size 8})]
|
|
||||||
(conj result (count (jdbc/execute! t ["select * from fruit"]))))))))
|
|
||||||
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"])))))
|
|
||||||
(testing "large batch insert"
|
|
||||||
(when-not (or (jtds?) (sqlite?))
|
|
||||||
(is (= [1 1 1 1 1 1 1 1 1 13]
|
(is (= [1 1 1 1 1 1 1 1 1 13]
|
||||||
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
||||||
(with-open [ps (jdbc/prepare t ["
|
(with-open [ps (jdbc/prepare t ["
|
||||||
|
|
@ -91,33 +55,70 @@ INSERT INTO fruit (name, appearance) VALUES (?,?)
|
||||||
["fruit7" "seven"]
|
["fruit7" "seven"]
|
||||||
["fruit8" "eight"]
|
["fruit8" "eight"]
|
||||||
["fruit9" "nine"]]
|
["fruit9" "nine"]]
|
||||||
{:batch-size 4
|
{:batch-size 3})]
|
||||||
:large true})]
|
|
||||||
(conj result (count (jdbc/execute! t ["select * from fruit"]))))))))
|
(conj result (count (jdbc/execute! t ["select * from fruit"]))))))))
|
||||||
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"]))))))
|
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"])))))
|
||||||
(testing "return generated keys"
|
(testing "big batch insert"
|
||||||
(when-not (or (mssql?) (sqlite?))
|
(is (= [1 1 1 1 1 1 1 1 1 13]
|
||||||
(let [results
|
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
||||||
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
(with-open [ps (jdbc/prepare t ["
|
||||||
(with-open [ps (jdbc/prepare t ["
|
INSERT INTO fruit (name, appearance) VALUES (?,?)
|
||||||
|
"])]
|
||||||
|
(let [result (prep/execute-batch! ps [["fruit1" "one"]
|
||||||
|
["fruit2" "two"]
|
||||||
|
["fruit3" "three"]
|
||||||
|
["fruit4" "four"]
|
||||||
|
["fruit5" "five"]
|
||||||
|
["fruit6" "six"]
|
||||||
|
["fruit7" "seven"]
|
||||||
|
["fruit8" "eight"]
|
||||||
|
["fruit9" "nine"]]
|
||||||
|
{:batch-size 8})]
|
||||||
|
(conj result (count (jdbc/execute! t ["select * from fruit"]))))))))
|
||||||
|
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"])))))
|
||||||
|
(testing "large batch insert"
|
||||||
|
(when-not (or (jtds?) (sqlite?))
|
||||||
|
(is (= [1 1 1 1 1 1 1 1 1 13]
|
||||||
|
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
||||||
|
(with-open [ps (jdbc/prepare t ["
|
||||||
|
INSERT INTO fruit (name, appearance) VALUES (?,?)
|
||||||
|
"])]
|
||||||
|
(let [result (prep/execute-batch! ps [["fruit1" "one"]
|
||||||
|
["fruit2" "two"]
|
||||||
|
["fruit3" "three"]
|
||||||
|
["fruit4" "four"]
|
||||||
|
["fruit5" "five"]
|
||||||
|
["fruit6" "six"]
|
||||||
|
["fruit7" "seven"]
|
||||||
|
["fruit8" "eight"]
|
||||||
|
["fruit9" "nine"]]
|
||||||
|
{:batch-size 4
|
||||||
|
:large true})]
|
||||||
|
(conj result (count (jdbc/execute! t ["select * from fruit"]))))))))
|
||||||
|
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"]))))))
|
||||||
|
(testing "return generated keys"
|
||||||
|
(when-not (or (mssql?) (sqlite?))
|
||||||
|
(let [results
|
||||||
|
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
||||||
|
(with-open [ps (jdbc/prepare t ["
|
||||||
INSERT INTO fruit (name, appearance) VALUES (?,?)
|
INSERT INTO fruit (name, appearance) VALUES (?,?)
|
||||||
"]
|
"]
|
||||||
{:return-keys true})]
|
{:return-keys true})]
|
||||||
(let [result (prep/execute-batch! ps [["fruit1" "one"]
|
(let [result (prep/execute-batch! ps [["fruit1" "one"]
|
||||||
["fruit2" "two"]
|
["fruit2" "two"]
|
||||||
["fruit3" "three"]
|
["fruit3" "three"]
|
||||||
["fruit4" "four"]
|
["fruit4" "four"]
|
||||||
["fruit5" "five"]
|
["fruit5" "five"]
|
||||||
["fruit6" "six"]
|
["fruit6" "six"]
|
||||||
["fruit7" "seven"]
|
["fruit7" "seven"]
|
||||||
["fruit8" "eight"]
|
["fruit8" "eight"]
|
||||||
["fruit9" "nine"]]
|
["fruit9" "nine"]]
|
||||||
{:batch-size 4
|
{:batch-size 4
|
||||||
:return-generated-keys true})]
|
:return-generated-keys true})]
|
||||||
(conj result (count (jdbc/execute! t ["select * from fruit"]))))))]
|
(conj result (count (jdbc/execute! t ["select * from fruit"]))))))]
|
||||||
(is (= 13 (last results)))
|
(is (= 13 (last results)))
|
||||||
(is (every? map? (butlast results)))
|
(is (every? map? (butlast results)))
|
||||||
;; Derby and SQLite only return one generated key per batch so there
|
;; Derby and SQLite only return one generated key per batch so there
|
||||||
;; are only three keys, plus the overall count here:
|
;; are only three keys, plus the overall count here:
|
||||||
(is (< 3 (count results))))
|
(is (< 3 (count results))))
|
||||||
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"])))))))
|
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"]))))))))
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@
|
||||||
[next.jdbc.protocols :as p]
|
[next.jdbc.protocols :as p]
|
||||||
[next.jdbc.result-set :as rs]
|
[next.jdbc.result-set :as rs]
|
||||||
[next.jdbc.specs :as specs]
|
[next.jdbc.specs :as specs]
|
||||||
[next.jdbc.test-fixtures :refer [with-test-db ds column
|
[next.jdbc.test-fixtures :refer [with-test-db ds column index col-kw
|
||||||
default-options
|
default-options
|
||||||
derby? mssql? mysql? postgres?]])
|
derby? mssql? mysql? postgres? xtdb?]])
|
||||||
(:import (java.sql ResultSet ResultSetMetaData)))
|
(:import (java.sql ResultSet ResultSetMetaData)))
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
@ -93,7 +93,7 @@
|
||||||
(deftest test-map-row-builder
|
(deftest test-map-row-builder
|
||||||
(testing "default row builder"
|
(testing "default row builder"
|
||||||
(let [row (p/-execute-one (ds)
|
(let [row (p/-execute-one (ds)
|
||||||
["select * from fruit where id = ?" 1]
|
[(str "select * from fruit where " (index) " = ?") 1]
|
||||||
(default-options))]
|
(default-options))]
|
||||||
(is (map? row))
|
(is (map? row))
|
||||||
(is (contains? row (column :FRUIT/GRADE)))
|
(is (contains? row (column :FRUIT/GRADE)))
|
||||||
|
|
@ -101,7 +101,7 @@
|
||||||
(is (= 1 ((column :FRUIT/ID) row)))
|
(is (= 1 ((column :FRUIT/ID) row)))
|
||||||
(is (= "Apple" ((column :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"]
|
[(str "select * from fruit order by " (index))]
|
||||||
(default-options))]
|
(default-options))]
|
||||||
(is (every? map? rs))
|
(is (every? map? rs))
|
||||||
(is (= 1 ((column :FRUIT/ID) (first rs))))
|
(is (= 1 ((column :FRUIT/ID) (first rs))))
|
||||||
|
|
@ -110,7 +110,7 @@
|
||||||
(is (= "Orange" ((column :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]
|
[(str "select * from fruit where " (index) " = ?") 2]
|
||||||
{:builder-fn rs/as-unqualified-maps})]
|
{:builder-fn rs/as-unqualified-maps})]
|
||||||
(is (map? row))
|
(is (map? row))
|
||||||
(is (contains? row (column :COST)))
|
(is (contains? row (column :COST)))
|
||||||
|
|
@ -119,7 +119,7 @@
|
||||||
(is (= "Banana" ((column :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]
|
[(str "select * from fruit where " (index) " = ?") 3]
|
||||||
(assoc (default-options)
|
(assoc (default-options)
|
||||||
:builder-fn rs/as-lower-maps))]
|
:builder-fn rs/as-lower-maps))]
|
||||||
(is (map? row))
|
(is (map? row))
|
||||||
|
|
@ -129,33 +129,33 @@
|
||||||
(is (= "Peach" (:fruit/name row)))))
|
(is (= "Peach" (:fruit/name row)))))
|
||||||
(testing "unqualified lower-case row builder"
|
(testing "unqualified lower-case row builder"
|
||||||
(let [row (p/-execute-one (ds)
|
(let [row (p/-execute-one (ds)
|
||||||
["select * from fruit where id = ?" 4]
|
[(str "select * from fruit where " (index) " = ?") 4]
|
||||||
{:builder-fn rs/as-unqualified-lower-maps})]
|
{:builder-fn rs/as-unqualified-lower-maps})]
|
||||||
(is (map? row))
|
(is (map? row))
|
||||||
(is (= 4 (:id row)))
|
(is (= 4 (:id row)))
|
||||||
(is (= "Orange" (:name row)))))
|
(is (= "Orange" (:name row)))))
|
||||||
(testing "kebab-case row builder"
|
(testing "kebab-case row builder"
|
||||||
(let [row (p/-execute-one (ds)
|
(let [row (p/-execute-one (ds)
|
||||||
["select id,name,appearance as looks_like from fruit where id = ?" 3]
|
[(str "select " (index) ",name,appearance as looks_like from fruit where " (index) " = ?") 3]
|
||||||
(assoc (default-options)
|
(assoc (default-options)
|
||||||
:builder-fn rs/as-kebab-maps))]
|
:builder-fn rs/as-kebab-maps))]
|
||||||
(is (map? row))
|
(is (map? row))
|
||||||
(is (contains? row :fruit/looks-like))
|
(is (contains? row (col-kw :fruit/looks-like)))
|
||||||
(is (nil? (:fruit/looks-like row)))
|
(is (nil? ((col-kw :fruit/looks-like) row)))
|
||||||
(is (= 3 (:fruit/id row)))
|
(is (= 3 ((col-kw :fruit/id) row)))
|
||||||
(is (= "Peach" (:fruit/name row)))))
|
(is (= "Peach" ((col-kw :fruit/name) row)))))
|
||||||
(testing "unqualified kebab-case row builder"
|
(testing "unqualified kebab-case row builder"
|
||||||
(let [row (p/-execute-one (ds)
|
(let [row (p/-execute-one (ds)
|
||||||
["select id,name,appearance as looks_like from fruit where id = ?" 4]
|
[(str "select " (index) ",name,appearance as looks_like from fruit where " (index) " = ?") 4]
|
||||||
{:builder-fn rs/as-unqualified-kebab-maps})]
|
{:builder-fn rs/as-unqualified-kebab-maps})]
|
||||||
(is (map? row))
|
(is (map? row))
|
||||||
(is (contains? row :looks-like))
|
(is (contains? row :looks-like))
|
||||||
(is (= "juicy" (:looks-like row)))
|
(is (= "juicy" (:looks-like row)))
|
||||||
(is (= 4 (:id row)))
|
(is (= 4 ((col-kw :id) row)))
|
||||||
(is (= "Orange" (:name row)))))
|
(is (= "Orange" (:name row)))))
|
||||||
(testing "custom row builder 1"
|
(testing "custom row builder 1"
|
||||||
(let [row (p/-execute-one (ds)
|
(let [row (p/-execute-one (ds)
|
||||||
["select fruit.*, id + 100 as newid from fruit where id = ?" 3]
|
[(str "select fruit.*, " (index) " + 100 as newid from fruit where " (index) " = ?") 3]
|
||||||
(assoc (default-options)
|
(assoc (default-options)
|
||||||
:builder-fn rs/as-modified-maps
|
:builder-fn rs/as-modified-maps
|
||||||
:label-fn str/lower-case
|
:label-fn str/lower-case
|
||||||
|
|
@ -181,7 +181,7 @@
|
||||||
(is (= "Peach" (:vegetable/name row)))))
|
(is (= "Peach" (:vegetable/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]
|
[(str "select * from fruit where " (index) " = ?") 3]
|
||||||
(assoc
|
(assoc
|
||||||
(default-options)
|
(default-options)
|
||||||
:builder-fn (rs/as-maps-adapter
|
:builder-fn (rs/as-maps-adapter
|
||||||
|
|
@ -207,7 +207,7 @@
|
||||||
(fn [^ResultSet rs _ ^Integer i]
|
(fn [^ResultSet rs _ ^Integer i]
|
||||||
(.getObject rs i)))
|
(.getObject rs i)))
|
||||||
row (p/-execute-one (ds)
|
row (p/-execute-one (ds)
|
||||||
["select * from fruit where id = ?" 3]
|
[(str "select * from fruit where " (index) " = ?") 3]
|
||||||
(assoc
|
(assoc
|
||||||
(default-options)
|
(default-options)
|
||||||
:builder-fn (rs/as-maps-adapter
|
:builder-fn (rs/as-maps-adapter
|
||||||
|
|
@ -299,31 +299,31 @@
|
||||||
(testing "no row builder is used"
|
(testing "no row builder is used"
|
||||||
(is (= [true]
|
(is (= [true]
|
||||||
(into [] (map map?) ; it looks like a real map now
|
(into [] (map map?) ; it looks like a real map now
|
||||||
(p/-execute (ds) ["select * from fruit where id = ?" 1]
|
(p/-execute (ds) [(str "select * from fruit where " (index) " = ?") 1]
|
||||||
{:builder-fn (constantly nil)}))))
|
{:builder-fn (constantly nil)}))))
|
||||||
(is (= ["Apple"]
|
(is (= ["Apple"]
|
||||||
(into [] (map :name) ; keyword selection works
|
(into [] (map :name) ; keyword selection works
|
||||||
(p/-execute (ds) ["select * from fruit where id = ?" 1]
|
(p/-execute (ds) [(str "select * from fruit where " (index) " = ?") 1]
|
||||||
{:builder-fn (constantly nil)}))))
|
{:builder-fn (constantly nil)}))))
|
||||||
(is (= [[2 [:name "Banana"]]]
|
(is (= [[2 [:name "Banana"]]]
|
||||||
(into [] (map (juxt #(get % "id") ; get by string key works
|
(into [] (map (juxt #(get % "id") ; get by string key works
|
||||||
#(find % :name))) ; get MapEntry works
|
#(find % :name))) ; get MapEntry works
|
||||||
(p/-execute (ds) ["select * from fruit where id = ?" 2]
|
(p/-execute (ds) [(str "select * from fruit where " (index) " = ?") 2]
|
||||||
{:builder-fn (constantly nil)}))))
|
{:builder-fn (constantly nil)}))))
|
||||||
(is (= [{:id 3 :name "Peach"}]
|
(is (= [{:id 3 :name "Peach"}]
|
||||||
(into [] (map #(select-keys % [:id :name])) ; select-keys works
|
(into [] (map #(select-keys % [:id :name])) ; select-keys works
|
||||||
(p/-execute (ds) ["select * from fruit where id = ?" 3]
|
(p/-execute (ds) [(str "select * from fruit where " (index) " = ?") 3]
|
||||||
{:builder-fn (constantly nil)}))))
|
{:builder-fn (constantly nil)}))))
|
||||||
(is (= [[:orange 4]]
|
(is (= [[:orange 4]]
|
||||||
(into [] (map #(vector (if (contains? % :name) ; contains works
|
(into [] (map #(vector (if (contains? % :name) ; contains works
|
||||||
(keyword (str/lower-case (:name %)))
|
(keyword (str/lower-case (:name %)))
|
||||||
:unnamed)
|
:unnamed)
|
||||||
(get % :id 0))) ; get with not-found works
|
(get % :id 0))) ; get with not-found works
|
||||||
(p/-execute (ds) ["select * from fruit where id = ?" 4]
|
(p/-execute (ds) [(str "select * from fruit where " (index) " = ?") 4]
|
||||||
{:builder-fn (constantly nil)}))))
|
{:builder-fn (constantly nil)}))))
|
||||||
(is (= [{}]
|
(is (= [{}]
|
||||||
(into [] (map empty) ; return empty map without building
|
(into [] (map empty) ; return empty map without building
|
||||||
(p/-execute (ds) ["select * from fruit where id = ?" 1]
|
(p/-execute (ds) [(str "select * from fruit where " (index) " = ?") 1]
|
||||||
{:builder-fn (constantly nil)})))))
|
{:builder-fn (constantly nil)})))))
|
||||||
(testing "count does not build a map"
|
(testing "count does not build a map"
|
||||||
(let [count-builder (fn [_1 _2]
|
(let [count-builder (fn [_1 _2]
|
||||||
|
|
@ -331,7 +331,7 @@
|
||||||
(column-count [_] 13)))]
|
(column-count [_] 13)))]
|
||||||
(is (= [13]
|
(is (= [13]
|
||||||
(into [] (map count) ; count relies on columns, not row fields
|
(into [] (map count) ; count relies on columns, not row fields
|
||||||
(p/-execute (ds) ["select * from fruit where id = ?" 1]
|
(p/-execute (ds) [(str "select * from fruit where " (index) " = ?") 1]
|
||||||
{:builder-fn count-builder}))))))
|
{:builder-fn count-builder}))))))
|
||||||
(testing "assoc, dissoc, cons, seq, and = build maps"
|
(testing "assoc, dissoc, cons, seq, and = build maps"
|
||||||
(is (map? (reduce (fn [_ row] (reduced (assoc row :x 1)))
|
(is (map? (reduce (fn [_ row] (reduced (assoc row :x 1)))
|
||||||
|
|
@ -467,7 +467,7 @@
|
||||||
metadata))))
|
metadata))))
|
||||||
|
|
||||||
(deftest clob-reading
|
(deftest clob-reading
|
||||||
(when-not (or (mssql?) (mysql?) (postgres?)) ; no clob in these
|
(when-not (or (mssql?) (mysql?) (postgres?) (xtdb?)) ; no clob in these
|
||||||
(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"] {})
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,15 @@
|
||||||
|
|
||||||
(ns next.jdbc.sql-test
|
(ns next.jdbc.sql-test
|
||||||
"Tests for the syntactic sugar SQL functions."
|
"Tests for the syntactic sugar SQL functions."
|
||||||
(:require [clojure.test :refer [deftest is testing use-fixtures]]
|
(:require
|
||||||
[next.jdbc :as jdbc]
|
[clojure.test :refer [deftest is testing use-fixtures]]
|
||||||
[next.jdbc.specs :as specs]
|
[next.jdbc :as jdbc]
|
||||||
[next.jdbc.sql :as sql]
|
[next.jdbc.specs :as specs]
|
||||||
[next.jdbc.test-fixtures
|
[next.jdbc.sql :as sql]
|
||||||
:refer [with-test-db ds column default-options
|
[next.jdbc.test-fixtures
|
||||||
derby? jtds? maria? mssql? mysql? postgres? sqlite?]]
|
:refer [column col-kw default-options derby? ds index
|
||||||
[next.jdbc.types :refer [as-other as-real as-varchar]]))
|
jtds? maria? mssql? mysql? postgres? sqlite? with-test-db xtdb?]]
|
||||||
|
[next.jdbc.types :refer [as-other as-real as-varchar]]))
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
||||||
|
|
@ -76,8 +77,8 @@
|
||||||
|
|
||||||
(deftest test-get-by-id
|
(deftest test-get-by-id
|
||||||
(let [ds-opts (jdbc/with-options (ds) (default-options))]
|
(let [ds-opts (jdbc/with-options (ds) (default-options))]
|
||||||
(is (nil? (sql/get-by-id ds-opts :fruit -1)))
|
(is (nil? (sql/get-by-id ds-opts :fruit -1 (col-kw :id) {})))
|
||||||
(let [row (sql/get-by-id ds-opts :fruit 3)]
|
(let [row (sql/get-by-id ds-opts :fruit 3 (col-kw :id) {})]
|
||||||
(is (map? row))
|
(is (map? row))
|
||||||
(is (= "Peach" ((column :FRUIT/NAME) row))))
|
(is (= "Peach" ((column :FRUIT/NAME) row))))
|
||||||
(let [row (sql/get-by-id ds-opts :fruit "juicy" :appearance {})]
|
(let [row (sql/get-by-id ds-opts :fruit "juicy" :appearance {})]
|
||||||
|
|
@ -92,19 +93,19 @@
|
||||||
(let [ds-opts (jdbc/with-options (ds) (default-options))]
|
(let [ds-opts (jdbc/with-options (ds) (default-options))]
|
||||||
(try
|
(try
|
||||||
(is (= {:next.jdbc/update-count 1}
|
(is (= {:next.jdbc/update-count 1}
|
||||||
(sql/update! ds-opts :fruit {:appearance "brown"} {:id 2})))
|
(sql/update! ds-opts :fruit {:appearance "brown"} {(col-kw :id) 2})))
|
||||||
(is (= "brown" ((column :FRUIT/APPEARANCE)
|
(is (= "brown" ((column :FRUIT/APPEARANCE)
|
||||||
(sql/get-by-id ds-opts :fruit 2))))
|
(sql/get-by-id ds-opts :fruit 2 (col-kw :id) {}))))
|
||||||
(finally
|
(finally
|
||||||
(sql/update! ds-opts :fruit {:appearance "yellow"} {:id 2})))
|
(sql/update! ds-opts :fruit {:appearance "yellow"} {(col-kw :id) 2})))
|
||||||
(try
|
(try
|
||||||
(is (= {:next.jdbc/update-count 1}
|
(is (= {:next.jdbc/update-count 1}
|
||||||
(sql/update! ds-opts :fruit {:appearance "green"}
|
(sql/update! ds-opts :fruit {:appearance "green"}
|
||||||
["name = ?" "Banana"])))
|
["name = ?" "Banana"])))
|
||||||
(is (= "green" ((column :FRUIT/APPEARANCE)
|
(is (= "green" ((column :FRUIT/APPEARANCE)
|
||||||
(sql/get-by-id ds-opts :fruit 2))))
|
(sql/get-by-id ds-opts :fruit 2 (col-kw :id) {}))))
|
||||||
(finally
|
(finally
|
||||||
(sql/update! ds-opts :fruit {:appearance "yellow"} {:id 2})))))
|
(sql/update! ds-opts :fruit {:appearance "yellow"} {(col-kw :id) 2})))))
|
||||||
|
|
||||||
(deftest test-insert-delete
|
(deftest test-insert-delete
|
||||||
(let [new-key (cond (derby?) :1
|
(let [new-key (cond (derby?) :1
|
||||||
|
|
@ -113,18 +114,23 @@
|
||||||
(mssql?) :GENERATED_KEYS
|
(mssql?) :GENERATED_KEYS
|
||||||
(mysql?) :GENERATED_KEY
|
(mysql?) :GENERATED_KEY
|
||||||
(postgres?) :fruit/id
|
(postgres?) :fruit/id
|
||||||
|
(xtdb?) :_id
|
||||||
:else :FRUIT/ID)]
|
:else :FRUIT/ID)]
|
||||||
(testing "single insert/delete"
|
(testing "single insert/delete"
|
||||||
(is (== 5 (new-key (sql/insert! (ds) :fruit
|
(is (== 5 (new-key (doto
|
||||||
{:name (as-varchar "Kiwi")
|
(sql/insert! (ds) :fruit
|
||||||
:appearance "green & fuzzy"
|
(cond-> {:name (as-varchar "Kiwi")
|
||||||
:cost 100 :grade (as-real 99.9)}
|
:appearance "green & fuzzy"
|
||||||
{:suffix
|
:cost 100 :grade (as-real 99.9)}
|
||||||
(when (sqlite?)
|
(xtdb?)
|
||||||
"RETURNING *")}))))
|
(assoc :_id 5))
|
||||||
|
{:suffix
|
||||||
|
(when (sqlite?)
|
||||||
|
"RETURNING *")})
|
||||||
|
(println (ds))))))
|
||||||
(is (= 5 (count (sql/query (ds) ["select * from fruit"]))))
|
(is (= 5 (count (sql/query (ds) ["select * from fruit"]))))
|
||||||
(is (= {:next.jdbc/update-count 1}
|
(is (= {:next.jdbc/update-count 1}
|
||||||
(sql/delete! (ds) :fruit {:id 5})))
|
(sql/delete! (ds) :fruit {(col-kw :id) 5})))
|
||||||
(is (= 4 (count (sql/query (ds) ["select * from fruit"])))))
|
(is (= 4 (count (sql/query (ds) ["select * from fruit"])))))
|
||||||
(testing "multiple insert/delete"
|
(testing "multiple insert/delete"
|
||||||
(is (= (cond (derby?)
|
(is (= (cond (derby?)
|
||||||
|
|
@ -137,19 +143,22 @@
|
||||||
[6 7 8])
|
[6 7 8])
|
||||||
(mapv new-key
|
(mapv new-key
|
||||||
(sql/insert-multi! (ds) :fruit
|
(sql/insert-multi! (ds) :fruit
|
||||||
[:name :appearance :cost :grade]
|
(cond->> [:name :appearance :cost :grade]
|
||||||
[["Kiwi" "green & fuzzy" 100 99.9]
|
(xtdb?) (cons :_id))
|
||||||
["Grape" "black" 10 50]
|
(cond->> [["Kiwi" "green & fuzzy" 100 99.9]
|
||||||
["Lemon" "yellow" 20 9.9]]
|
["Grape" "black" 10 50]
|
||||||
|
["Lemon" "yellow" 20 9.9]]
|
||||||
|
(xtdb?)
|
||||||
|
(map cons [6 7 8]))
|
||||||
{:suffix
|
{:suffix
|
||||||
(when (sqlite?)
|
(when (sqlite?)
|
||||||
"RETURNING *")}))))
|
"RETURNING *")}))))
|
||||||
(is (= 7 (count (sql/query (ds) ["select * from fruit"]))))
|
(is (= 7 (count (sql/query (ds) ["select * from fruit"]))))
|
||||||
(is (= {:next.jdbc/update-count 1}
|
(is (= {:next.jdbc/update-count 1}
|
||||||
(sql/delete! (ds) :fruit {:id 6})))
|
(sql/delete! (ds) :fruit {(col-kw :id) 6})))
|
||||||
(is (= 6 (count (sql/query (ds) ["select * from fruit"]))))
|
(is (= 6 (count (sql/query (ds) ["select * from fruit"]))))
|
||||||
(is (= {:next.jdbc/update-count 2}
|
(is (= {:next.jdbc/update-count 2}
|
||||||
(sql/delete! (ds) :fruit ["id > ?" 4])))
|
(sql/delete! (ds) :fruit [(str (index) " > ?") 4])))
|
||||||
(is (= 4 (count (sql/query (ds) ["select * from fruit"])))))
|
(is (= 4 (count (sql/query (ds) ["select * from fruit"])))))
|
||||||
(testing "multiple insert/delete with sequential cols/rows" ; per #43
|
(testing "multiple insert/delete with sequential cols/rows" ; per #43
|
||||||
(is (= (cond (derby?)
|
(is (= (cond (derby?)
|
||||||
|
|
@ -162,19 +171,22 @@
|
||||||
[9 10 11])
|
[9 10 11])
|
||||||
(mapv new-key
|
(mapv new-key
|
||||||
(sql/insert-multi! (ds) :fruit
|
(sql/insert-multi! (ds) :fruit
|
||||||
'(:name :appearance :cost :grade)
|
(cond->> '(:name :appearance :cost :grade)
|
||||||
'(("Kiwi" "green & fuzzy" 100 99.9)
|
(xtdb?) (cons :_id))
|
||||||
("Grape" "black" 10 50)
|
(cond->> '(("Kiwi" "green & fuzzy" 100 99.9)
|
||||||
("Lemon" "yellow" 20 9.9))
|
("Grape" "black" 10 50)
|
||||||
|
("Lemon" "yellow" 20 9.9))
|
||||||
|
(xtdb?)
|
||||||
|
(map cons [9 10 11]))
|
||||||
{:suffix
|
{:suffix
|
||||||
(when (sqlite?)
|
(when (sqlite?)
|
||||||
"RETURNING *")}))))
|
"RETURNING *")}))))
|
||||||
(is (= 7 (count (sql/query (ds) ["select * from fruit"]))))
|
(is (= 7 (count (sql/query (ds) ["select * from fruit"]))))
|
||||||
(is (= {:next.jdbc/update-count 1}
|
(is (= {:next.jdbc/update-count 1}
|
||||||
(sql/delete! (ds) :fruit {:id 9})))
|
(sql/delete! (ds) :fruit {(col-kw :id) 9})))
|
||||||
(is (= 6 (count (sql/query (ds) ["select * from fruit"]))))
|
(is (= 6 (count (sql/query (ds) ["select * from fruit"]))))
|
||||||
(is (= {:next.jdbc/update-count 2}
|
(is (= {:next.jdbc/update-count 2}
|
||||||
(sql/delete! (ds) :fruit ["id > ?" 4])))
|
(sql/delete! (ds) :fruit [(str (index) " > ?") 4])))
|
||||||
(is (= 4 (count (sql/query (ds) ["select * from fruit"])))))
|
(is (= 4 (count (sql/query (ds) ["select * from fruit"])))))
|
||||||
(testing "multiple insert/delete with maps"
|
(testing "multiple insert/delete with maps"
|
||||||
(is (= (cond (derby?)
|
(is (= (cond (derby?)
|
||||||
|
|
@ -187,27 +199,29 @@
|
||||||
[12 13 14])
|
[12 13 14])
|
||||||
(mapv new-key
|
(mapv new-key
|
||||||
(sql/insert-multi! (ds) :fruit
|
(sql/insert-multi! (ds) :fruit
|
||||||
[{:name "Kiwi"
|
(cond->> [{:name "Kiwi"
|
||||||
:appearance "green & fuzzy"
|
:appearance "green & fuzzy"
|
||||||
:cost 100
|
:cost 100
|
||||||
:grade 99.9}
|
:grade 99.9}
|
||||||
{:name "Grape"
|
{:name "Grape"
|
||||||
:appearance "black"
|
:appearance "black"
|
||||||
:cost 10
|
:cost 10
|
||||||
:grade 50}
|
:grade 50}
|
||||||
{:name "Lemon"
|
{:name "Lemon"
|
||||||
:appearance "yellow"
|
:appearance "yellow"
|
||||||
:cost 20
|
:cost 20
|
||||||
:grade 9.9}]
|
:grade 9.9}]
|
||||||
|
(xtdb?)
|
||||||
|
(map #(assoc %2 :_id %1) [12 13 14]))
|
||||||
{:suffix
|
{:suffix
|
||||||
(when (sqlite?)
|
(when (sqlite?)
|
||||||
"RETURNING *")}))))
|
"RETURNING *")}))))
|
||||||
(is (= 7 (count (sql/query (ds) ["select * from fruit"]))))
|
(is (= 7 (count (sql/query (ds) ["select * from fruit"]))))
|
||||||
(is (= {:next.jdbc/update-count 1}
|
(is (= {:next.jdbc/update-count 1}
|
||||||
(sql/delete! (ds) :fruit {:id 12})))
|
(sql/delete! (ds) :fruit {(col-kw :id) 12})))
|
||||||
(is (= 6 (count (sql/query (ds) ["select * from fruit"]))))
|
(is (= 6 (count (sql/query (ds) ["select * from fruit"]))))
|
||||||
(is (= {:next.jdbc/update-count 2}
|
(is (= {:next.jdbc/update-count 2}
|
||||||
(sql/delete! (ds) :fruit ["id > ?" 10])))
|
(sql/delete! (ds) :fruit [(str (index) " > ?") 10])))
|
||||||
(is (= 4 (count (sql/query (ds) ["select * from fruit"])))))
|
(is (= 4 (count (sql/query (ds) ["select * from fruit"])))))
|
||||||
(testing "empty insert-multi!" ; per #44 and #264
|
(testing "empty insert-multi!" ; per #44 and #264
|
||||||
(is (= [] (sql/insert-multi! (ds) :fruit
|
(is (= [] (sql/insert-multi! (ds) :fruit
|
||||||
|
|
@ -255,7 +269,7 @@
|
||||||
|
|
||||||
(deftest array-in
|
(deftest array-in
|
||||||
(when (postgres?)
|
(when (postgres?)
|
||||||
(let [data (sql/find-by-keys (ds) :fruit ["id = any(?)" (int-array [1 2 3 4])])]
|
(let [data (sql/find-by-keys (ds) :fruit [(str (index) " = any(?)") (int-array [1 2 3 4])])]
|
||||||
(is (= 4 (count data))))))
|
(is (= 4 (count data))))))
|
||||||
|
|
||||||
(deftest enum-pg
|
(deftest enum-pg
|
||||||
|
|
|
||||||
|
|
@ -64,11 +64,17 @@
|
||||||
(def ^:private test-jtds
|
(def ^:private test-jtds
|
||||||
(when (System/getenv "NEXT_JDBC_TEST_MSSQL") test-jtds-map))
|
(when (System/getenv "NEXT_JDBC_TEST_MSSQL") test-jtds-map))
|
||||||
|
|
||||||
|
(def ^:private test-xtdb-map {:dbtype "xtdb"})
|
||||||
|
|
||||||
|
(def ^:private test-xtdb
|
||||||
|
(when (System/getenv "NEXT_JDBC_TEST_XTDB") test-xtdb-map))
|
||||||
|
|
||||||
(def ^:private test-db-specs
|
(def ^:private test-db-specs
|
||||||
(cond-> [test-derby test-h2-mem test-h2 test-hsql test-sqlite]
|
(cond-> [test-derby test-h2-mem test-h2 test-hsql test-sqlite]
|
||||||
test-postgres (conj test-postgres)
|
test-postgres (conj test-postgres)
|
||||||
test-mysql (conj test-mysql)
|
test-mysql (conj test-mysql)
|
||||||
test-mssql (conj test-mssql test-jtds)))
|
test-mssql (conj test-mssql test-jtds)
|
||||||
|
test-xtdb (conj test-xtdb)))
|
||||||
|
|
||||||
(def ^:private test-db-spec (atom nil))
|
(def ^:private test-db-spec (atom nil))
|
||||||
|
|
||||||
|
|
@ -86,19 +92,34 @@
|
||||||
|
|
||||||
(defn postgres? [] (= "embedded-postgres" (:dbtype @test-db-spec)))
|
(defn postgres? [] (= "embedded-postgres" (:dbtype @test-db-spec)))
|
||||||
|
|
||||||
|
(defn xtdb? [] (= "xtdb" (:dbtype @test-db-spec)))
|
||||||
|
|
||||||
(defn sqlite? [] (= "sqlite" (:dbtype @test-db-spec)))
|
(defn sqlite? [] (= "sqlite" (:dbtype @test-db-spec)))
|
||||||
|
|
||||||
(defn stored-proc? [] (not (#{"derby" "h2" "h2:mem" "sqlite"} (:dbtype @test-db-spec))))
|
(defn stored-proc? [] (not (#{"derby" "h2" "h2:mem" "sqlite" "xtdb"}
|
||||||
|
(:dbtype @test-db-spec))))
|
||||||
|
|
||||||
(defn column [k]
|
(defn column [k]
|
||||||
(let [n (namespace k)]
|
(let [n (namespace k)]
|
||||||
(keyword (when n (cond (postgres?) (str/lower-case n)
|
(keyword (when n (cond (postgres?) (str/lower-case n)
|
||||||
(mssql?) (str/lower-case n)
|
(mssql?) (str/lower-case n)
|
||||||
(mysql?) (str/lower-case n)
|
(mysql?) (str/lower-case n)
|
||||||
|
(xtdb?) nil
|
||||||
:else n))
|
:else n))
|
||||||
(cond (postgres?) (str/lower-case (name k))
|
(cond (postgres?) (str/lower-case (name k))
|
||||||
|
(xtdb?) (let [c (str/lower-case (name k))]
|
||||||
|
(if (= "id" c) "_id" c))
|
||||||
:else (name k)))))
|
:else (name k)))))
|
||||||
|
|
||||||
|
(defn index []
|
||||||
|
(if (xtdb?) "_id" "id"))
|
||||||
|
|
||||||
|
(defn col-kw [k]
|
||||||
|
(if (xtdb?)
|
||||||
|
(let [n (name k)]
|
||||||
|
(if (= "id" n) :_id (keyword n)))
|
||||||
|
k))
|
||||||
|
|
||||||
(defn default-options []
|
(defn default-options []
|
||||||
(if (mssql?) ; so that we get table names back from queries
|
(if (mssql?) ; so that we get table names back from queries
|
||||||
{:result-type :scroll-insensitive :concurrency :read-only}
|
{:result-type :scroll-insensitive :concurrency :read-only}
|
||||||
|
|
@ -156,29 +177,51 @@
|
||||||
:else
|
:else
|
||||||
"AUTO_INCREMENT PRIMARY KEY")]
|
"AUTO_INCREMENT PRIMARY KEY")]
|
||||||
(with-open [con (jdbc/get-connection (ds))]
|
(with-open [con (jdbc/get-connection (ds))]
|
||||||
(when (stored-proc?)
|
(if (xtdb?) ; no DDL for creation
|
||||||
(try
|
(do
|
||||||
(jdbc/execute-one! con ["DROP PROCEDURE FRUITP"])
|
(try
|
||||||
(catch Throwable _)))
|
(do-commands con ["DELETE FROM fruit WHERE true"])
|
||||||
(try
|
(catch Throwable _))
|
||||||
(do-commands con [(str "DROP TABLE " fruit)])
|
(sql/insert-multi! con :fruit
|
||||||
(catch Exception _))
|
[:_id :name :appearance :cost]
|
||||||
(try
|
[[1 "Apple" "red" 59]]
|
||||||
(do-commands con [(str "DROP TABLE " btest)])
|
{:return-keys false})
|
||||||
(catch Exception _))
|
(sql/insert-multi! con :fruit
|
||||||
(when (postgres?)
|
[:_id :name :appearance :grade]
|
||||||
(try
|
[[2 "Banana" "yellow" 92.2]]
|
||||||
(do-commands con ["DROP TABLE LANG_TEST"])
|
{:return-keys false})
|
||||||
(catch Exception _))
|
(sql/insert-multi! con :fruit
|
||||||
(try
|
[:_id :name :cost :grade]
|
||||||
(do-commands con ["DROP TYPE LANGUAGE"])
|
[[3 "Peach" 139 90.0]]
|
||||||
(catch Exception _))
|
{:return-keys false})
|
||||||
(do-commands con ["CREATE TYPE LANGUAGE AS ENUM('en','fr','de')"])
|
(sql/insert-multi! con :fruit
|
||||||
(do-commands con ["
|
[:_id :name :appearance :cost :grade]
|
||||||
|
[[4 "Orange" "juicy" 89 88.6]]
|
||||||
|
{:return-keys false}))
|
||||||
|
(do
|
||||||
|
(when (stored-proc?)
|
||||||
|
(try
|
||||||
|
(jdbc/execute-one! con ["DROP PROCEDURE FRUITP"])
|
||||||
|
(catch Throwable _)))
|
||||||
|
(try
|
||||||
|
(do-commands con [(str "DROP TABLE " fruit)])
|
||||||
|
(catch Exception _))
|
||||||
|
(try
|
||||||
|
(do-commands con [(str "DROP TABLE " btest)])
|
||||||
|
(catch Exception _))
|
||||||
|
(when (postgres?)
|
||||||
|
(try
|
||||||
|
(do-commands con ["DROP TABLE LANG_TEST"])
|
||||||
|
(catch Exception _))
|
||||||
|
(try
|
||||||
|
(do-commands con ["DROP TYPE LANGUAGE"])
|
||||||
|
(catch Exception _))
|
||||||
|
(do-commands con ["CREATE TYPE LANGUAGE AS ENUM('en','fr','de')"])
|
||||||
|
(do-commands con ["
|
||||||
CREATE TABLE LANG_TEST (
|
CREATE TABLE LANG_TEST (
|
||||||
LANG LANGUAGE NOT NULL
|
LANG LANGUAGE NOT NULL
|
||||||
)"]))
|
)"]))
|
||||||
(do-commands con [(str "
|
(do-commands con [(str "
|
||||||
CREATE TABLE " fruit " (
|
CREATE TABLE " fruit " (
|
||||||
ID INTEGER " auto-inc-pk ",
|
ID INTEGER " auto-inc-pk ",
|
||||||
NAME VARCHAR(32),
|
NAME VARCHAR(32),
|
||||||
|
|
@ -186,28 +229,28 @@ CREATE TABLE " fruit " (
|
||||||
COST INT DEFAULT NULL,
|
COST INT DEFAULT NULL,
|
||||||
GRADE REAL DEFAULT NULL
|
GRADE REAL DEFAULT NULL
|
||||||
)")])
|
)")])
|
||||||
(let [created (atom false)]
|
(let [created (atom false)]
|
||||||
;; MS SQL Server does not support bool/boolean:
|
;; MS SQL Server does not support bool/boolean:
|
||||||
(doseq [btype ["BOOL" "BOOLEAN" "BIT"]]
|
(doseq [btype ["BOOL" "BOOLEAN" "BIT"]]
|
||||||
;; Derby does not support bit:
|
;; Derby does not support bit:
|
||||||
(doseq [bitty ["BIT" "SMALLINT"]]
|
(doseq [bitty ["BIT" "SMALLINT"]]
|
||||||
(try
|
(try
|
||||||
(when-not @created
|
(when-not @created
|
||||||
(do-commands con [(str "
|
(do-commands con [(str "
|
||||||
CREATE TABLE " btest " (
|
CREATE TABLE " btest " (
|
||||||
NAME VARCHAR(32),
|
NAME VARCHAR(32),
|
||||||
IS_IT " btype ",
|
IS_IT " btype ",
|
||||||
TWIDDLE " bitty "
|
TWIDDLE " bitty "
|
||||||
)")])
|
)")])
|
||||||
(reset! created true))
|
(reset! created true))
|
||||||
(catch Throwable _))))
|
(catch Throwable _))))
|
||||||
(when-not @created
|
(when-not @created
|
||||||
(println (:dbtype db) "failed btest creation")
|
(println (:dbtype db) "failed btest creation")
|
||||||
#_(throw (ex-info (str (:dbtype db) " has no boolean type?") {}))))
|
#_(throw (ex-info (str (:dbtype db) " has no boolean type?") {}))))
|
||||||
(when (stored-proc?)
|
(when (stored-proc?)
|
||||||
(let [[begin end] (if (postgres?) ["$$" "$$"] ["BEGIN" "END"])]
|
(let [[begin end] (if (postgres?) ["$$" "$$"] ["BEGIN" "END"])]
|
||||||
(try
|
(try
|
||||||
(do-commands con [(str "
|
(do-commands con [(str "
|
||||||
CREATE PROCEDURE FRUITP" (cond (hsqldb?) "() READS SQL DATA DYNAMIC RESULT SETS 2 "
|
CREATE PROCEDURE FRUITP" (cond (hsqldb?) "() READS SQL DATA DYNAMIC RESULT SETS 2 "
|
||||||
(mssql?) " AS "
|
(mssql?) " AS "
|
||||||
(postgres?) "() LANGUAGE SQL AS "
|
(postgres?) "() LANGUAGE SQL AS "
|
||||||
|
|
@ -223,15 +266,15 @@ CREATE PROCEDURE FRUITP" (cond (hsqldb?) "() READS SQL DATA DYNAMIC RESULT SETS
|
||||||
SELECT * FROM " fruit " WHERE GRADE >= 90.0;")) "
|
SELECT * FROM " fruit " WHERE GRADE >= 90.0;")) "
|
||||||
" end "
|
" end "
|
||||||
")])
|
")])
|
||||||
(catch Throwable t
|
(catch Throwable t
|
||||||
(println 'procedure (:dbtype db) (ex-message t))))))
|
(println 'procedure (:dbtype db) (ex-message t))))))
|
||||||
(sql/insert-multi! con :fruit
|
(sql/insert-multi! con :fruit
|
||||||
[:name :appearance :cost :grade]
|
[:name :appearance :cost :grade]
|
||||||
[["Apple" "red" 59 nil]
|
[["Apple" "red" 59 nil]
|
||||||
["Banana" "yellow" nil 92.2]
|
["Banana" "yellow" nil 92.2]
|
||||||
["Peach" nil 139 90.0]
|
["Peach" nil 139 90.0]
|
||||||
["Orange" "juicy" 89 88.6]]
|
["Orange" "juicy" 89 88.6]]
|
||||||
{:return-keys false})
|
{:return-keys false})))
|
||||||
(t)))))
|
(t)))))
|
||||||
|
|
||||||
(create-clojure-test)
|
(create-clojure-test)
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue