Merge develop -> nested-tx

This commit is contained in:
Sean Corfield 2020-06-22 17:00:01 -07:00
commit 7a06e9cca8
12 changed files with 237 additions and 141 deletions

View file

@ -2,17 +2,17 @@
Only accretive/fixative changes will be made from now on.
Changes made on master since 1.0.462:
* WIP: nested transaction support!
* Add tests for `"jtds"` database driver (against MS SQL Server), making it officially supported.
* Switch from OpenTable Embedded PostgreSQL to Zonky's version, so that testing can move forward from PostgreSQL 10.11 to 12.2.0.
* Fix potential reflection warnings caused by `next.jdbc.prepare/statement` being incorrectly type-hinted.
* Address #119 by clarifying realization actions in the docstrings for `row-number`, `column-names`, and `metadata`.
* Address #115 by adding equivalent of `db-do-commands` in the `clojure.java.jdbc` migration guide.
* Add log4j2 as a test dependency so that I have better control over logging (which makes debugging easier!).
## Stable Builds
* 2020-06-22 -- 1.0.475
* Add tests for `"jtds"` database driver (against MS SQL Server), making it officially supported.
* Switch from OpenTable Embedded PostgreSQL to Zonky's version, so that testing can move forward from PostgreSQL 10.11 to 12.2.0.
* Fix potential reflection warnings caused by `next.jdbc.prepare/statement` being incorrectly type-hinted.
* Address #122 by adding `next.jdbc.with-options` that lets you wrap up a connectable along with default options that should be applied to all operations on that connectable.
* Address #119 by clarifying realization actions in the docstrings for `row-number`, `column-names`, and `metadata`.
* Address #115 by adding equivalent of `db-do-commands` in the `clojure.java.jdbc` migration guide.
* Add log4j2 as a test dependency so that I have better control over logging (which makes debugging easier!).
* 2020-05-31 -- 1.0.462
* Addition of `next.jdbc.datafy` to provide more `datafy`/`nav` introspection (see the additional section in **datafy, nav, and :schema** for details).
* Addition of `next.jdbc.result-set/metadata` to provide (datafied) result set metadata within `plan`.

View file

@ -1,4 +1,4 @@
# next.jdbc [![CircleCI](https://circleci.com/gh/seancorfield/next-jdbc/tree/master.svg?style=svg)](https://circleci.com/gh/seancorfield/next-jdbc/tree/master)
# next.jdbc [![CircleCI](https://circleci.com/gh/seancorfield/next-jdbc/tree/develop.svg?style=svg)](https://circleci.com/gh/seancorfield/next-jdbc/tree/develop)
The next generation of `clojure.java.jdbc`: a new low-level Clojure wrapper for JDBC-based access to databases.
@ -6,7 +6,7 @@ The next generation of `clojure.java.jdbc`: a new low-level Clojure wrapper for
The latest versions on Clojars and on cljdoc:
[![Clojars Project](https://clojars.org/seancorfield/next.jdbc/latest-version.svg)](https://clojars.org/seancorfield/next.jdbc) [![cljdoc badge](https://cljdoc.org/badge/seancorfield/next.jdbc?1.0.462)](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT)
[![Clojars Project](https://clojars.org/seancorfield/next.jdbc/latest-version.svg)](https://clojars.org/seancorfield/next.jdbc) [![cljdoc badge](https://cljdoc.org/badge/seancorfield/next.jdbc?1.0.475)](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT)
The documentation on [cljdoc.org](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT) is for the current version of `next.jdbc`:
@ -14,7 +14,7 @@ The documentation on [cljdoc.org](https://cljdoc.org/d/seancorfield/next.jdbc/CU
* [Migrating from `clojure.java.jdbc`](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT/doc/migration-from-clojure-java-jdbc)
* Feedback via [issues](https://github.com/seancorfield/next-jdbc/issues) or in the [`#sql` channel on the Clojurians Slack](https://clojurians.slack.com/messages/C1Q164V29/details/) or the [`#sql` stream on the Clojurians Zulip](https://clojurians.zulipchat.com/#narrow/stream/152063-sql).
The documentation on GitHub is for **master** since the 1.0.462 release -- [see the CHANGELOG](https://github.com/seancorfield/next-jdbc/blob/master/CHANGELOG.md) and then read the [corresponding updated documentation](https://github.com/seancorfield/next-jdbc/tree/master/doc) on GitHub if you want.
The documentation on GitHub is for **develop** since the 1.0.475 release -- [see the CHANGELOG](https://github.com/seancorfield/next-jdbc/blob/develop/CHANGELOG.md) and then read the [corresponding updated documentation](https://github.com/seancorfield/next-jdbc/tree/develop/doc) on GitHub if you want.
This project follows the version scheme MAJOR.MINOR.COMMITS where MAJOR and MINOR provide some relative indication of the size of the change, but do not follow semantic versioning. In general, all changes endeavor to be non-breaking (by moving to new names rather than by breaking existing names). COMMITS is an ever-increasing counter of commits since the beginning of this repository.

View file

@ -9,16 +9,16 @@ It is designed to work with Clojure 1.10 or later, supports `datafy`/`nav`, and
You can add `next.jdbc` to your project with either:
```clojure
{seancorfield/next.jdbc {:mvn/version "1.0.462"}}
seancorfield/next.jdbc {:mvn/version "1.0.475"}
```
for `deps.edn` or:
```clojure
[seancorfield/next.jdbc "1.0.462"]
[seancorfield/next.jdbc "1.0.475"]
```
for `project.clj` or `build.boot`.
**In addition, you will need to add dependencies for the JDBC drivers you wish to use for whatever databases you are using.** You can see the drivers and versions that `next.jdbc` is tested against in [the project's `deps.edn` file](https://github.com/seancorfield/next-jdbc/blob/master/deps.edn#L10-L25), but many other JDBC drivers for other databases should also work (e.g., Oracle, Red Shift).
**In addition, you will need to add dependencies for the JDBC drivers you wish to use for whatever databases you are using.** You can see the drivers and versions that `next.jdbc` is tested against in [the project's `deps.edn` file](https://github.com/seancorfield/next-jdbc/blob/develop/deps.edn#L10-L27), but many other JDBC drivers for other databases should also work (e.g., Oracle, Red Shift).
## An Example REPL Session
@ -29,7 +29,7 @@ For the examples in this documentation, we will use a local H2 database on disk,
```clojure
;; deps.edn
{:deps {org.clojure/clojure {:mvn/version "1.10.1"}
seancorfield/next.jdbc {:mvn/version "1.0.462"}
seancorfield/next.jdbc {:mvn/version "1.0.475"}
com.h2database/h2 {:mvn/version "1.4.199"}}}
```
@ -108,10 +108,10 @@ user=> (jdbc/execute-one! ds ["
insert into address(name,email)
values('Someone Else','some@elsewhere.com')
"] {:return-keys true :builder-fn rs/as-unqualified-lower-maps})
{:id 2}
user=> (jdbc/execute-one! ds ["select * from address where id = ?" 2]
{:id 3}
user=> (jdbc/execute-one! ds ["select * from address where id = ?" 3]
{:builder-fn rs/as-unqualified-lower-maps})
{:id 2, :name "Someone Else", :email "some@elsewhere.com"}
{:id 3, :name "Someone Else", :email "some@elsewhere.com"}
user=>
```
@ -121,6 +121,23 @@ Relying on the default result set builder -- and table-qualified column names --
* If your SQL query joins tables in a way that produces duplicate column names, and you use unqualified column names, then those duplicated column names will conflict and you will get only one of them in your result -- use aliases in SQL (`as`) to make the column names distinct,
* If your SQL query joins a table to itself under different aliases, the _qualified_ column names will conflict because they are based on the underlying table name provided by the JDBC driver rather the alias you used in your query -- again, use aliases in SQL to make those column names distinct.
If you want to pass the same set of options into several operations, you can use `next.jdbc/with-options` to wrap your datasource (or connection) in a way that will pass "default options". Here's the example above rewritten with that:
```clojure
user=> (require '[next.jdbc.result-set :as rs])
nil
user=> (def ds-opts (jdbc/with-options ds {:builder-fn rs/as-unqualified-lower-maps}))
#'user/ds-opts
user=> (jdbc/execute-one! ds-opts ["
insert into address(name,email)
values('Someone Else','some@elsewhere.com')
"] {:return-keys true})
{:id 4}
user=> (jdbc/execute-one! ds-opts ["select * from address where id = ?" 4])
{:id 4, :name "Someone Else", :email "some@elsewhere.com"}
user=>
```
### `plan` & Reducing Result Sets
While the `execute!` and `execute-one!` functions are fine for retrieving result sets as data, most of the time you want to process that data efficiently without necessarily converting the entire result set into a Clojure data structure, so `next.jdbc` provides a SQL execution function that works with `reduce` and with transducers to consume the result set without the intermediate overhead of creating Clojure data structures for every row.
@ -269,6 +286,21 @@ If `with-transaction` is given a datasource, it will create and close the connec
You can read more about [working with transactions](/doc/transactions.md) further on in the documentation.
> Note: Because `get-datasource` and `get-connection` return plain JDBC objects (`javax.sql.DataSource` and `java.sql.Connection` respectively), `next.jdbc/with-options` cannot flow options across those calls, so if you are explicitly managing connections or transactions as above, you would need to have local bindings for the wrapped versions:
```clojure
(with-open [con (jdbc/get-connection ds)]
(let [con-opts (jdbc/with-options con some-options)]
(jdbc/execute! con-opts ...) ; committed
;; either use unwrapped version here or (:connectable con-opts)
(jdbc/with-transaction [tx con] ; will commit or rollback this group:
(let [tx-opts (jdbc/with-options tx some-options)]
(jdbc/execute! tx-opts ...)
(jdbc/execute! tx-opts ...)
(into [] (map :column) (jdbc/plan tx-opts ...))))
(jdbc/execute! con-opts ...))) ; committed
```
### Prepared Statement Caveat
Not all databases support using a `PreparedStatement` for every type of SQL operation. You might have to create a `java.sql.Statement` instead, directly from a `java.sql.Connection` and use that, without parameters, in `plan`, `execute!`, or `execute-one!`. See the following example:

View file

@ -20,11 +20,11 @@ If you used `:as-arrays? true`, you will most likely want to use a `:builder-fn`
### Option Handling
Because `clojure.java.jdbc` focuses on a hash map for the `db-spec` that is passed around, it can hold options that act as defaults for all operations on it. In addition, all operations in `clojure.java.jdbc` can accept a hash map of options and can pass those options down the call chain. In `next.jdbc`, `get-datasource`, `get-connection`, and `prepare` all produce Java objects that cannot have any extra options attached. On one hand, that means that you cannot provide "default options", and on the other hand it means you need to be a bit more careful to ensure that you pass the appropriate options to the appropriate function, since they cannot be passed through the call chain via the `db-spec`.
Because `clojure.java.jdbc` focuses on a hash map for the `db-spec` that is passed around, it can hold options that act as defaults for all operations on it. In addition, all operations in `clojure.java.jdbc` can accept a hash map of options and can pass those options down the call chain. In `next.jdbc`, `get-datasource`, `get-connection`, and `prepare` all produce Java objects that cannot have any extra options attached. On one hand, that means that it is harder to provide "default options", and on the other hand it means you need to be a bit more careful to ensure that you pass the appropriate options to the appropriate function, since they cannot be passed through the call chain via the `db-spec`. That's where `next.jdbc/with-options` can come in handy to wrap a connectable (generally a datasource or a connection) but be careful where you are managing connections and/or transactions directly, as mentioned in the [Getting Started](/doc/getting-started.md) guide.
In [All The Options](all-the-options.md), the appropriate options are shown for each function, as well as which options _will_ get passed down the call chain, e.g., if a function can open a connection, it will accept options for `get-connection`; if a function can build a result set, it will accept `:builder-fn`. However, `get-datasource`, `get-connection`, and `prepare` cannot propagate options any further because they produce Java objects as their results -- in particular, `prepare` can't accept `:builder-fn` because it doesn't build result sets: only `plan`, `execute-one!`, and `execute!` can use `:builder-fn`.
In particular, this means that you can't globally override the default options (as you could with `clojure.java.jdbc` by adding your preferred defaults to the db-spec itself). If the default options do not suit your usage and you really don't want to override them in every call, it is recommended that you provide a wrapper namespace that implements the subset of the dozen API functions (from `next.jdbc` and `next.jdbc.sql`) that you want to use, overriding their `opts` argument with your defaults.
In particular, this means that you can't globally override the default options (as you could with `clojure.java.jdbc` by adding your preferred defaults to the db-spec itself). If the default options do not suit your usage and you really don't want to override them in every call, it is recommended that you try to use `next.jdbc/with-options` first, and if that still doesn't satisfy you, write a wrapper namespace that implements the subset of the dozen API functions (from `next.jdbc` and `next.jdbc.sql`) that you want to use, overriding their `opts` argument with your defaults.
## Primary API
@ -37,7 +37,8 @@ In particular, this means that you can't globally override the default options (
* `execute!` -- has no direct equivalent in `clojure.java.jdbc` (but it can replace most uses of both `query` and `db-do-commands`),
* `execute-one!` -- has no equivalent in `clojure.java.jdbc` (but it can replace most uses of `query` that currently use `:result-set-fn first`),
* `transact` -- similar to `clojure.java.jdbc/db-transaction*`,
* `with-transaction` -- similar to `clojure.java.jdbc/with-db-transaction`.
* `with-transaction` -- similar to `clojure.java.jdbc/with-db-transaction`,
* `with-options` -- provides a way to specify "default options" over a group of operations, by wrapping the connectable (datasource or connection).
If you were using a bare `db-spec` hash map with `:dbtype`/`:dbname`, or a JDBC URI string everywhere, that should mostly work with `next.jdbc` since most functions accept a "connectable", but it would be better to create a datasource first, and then pass that around. Note that `clojure.java.jdbc` allowed the `jdbc:` prefix in a JDBC URI to be omitted but `next.jdbc` _requires that prefix!_

View file

@ -72,7 +72,7 @@ Only `execute!` expects this protocol to be implemented. `execute-one!` and `pla
The `as-*` functions described above are all implemented in terms of these protocols. They are passed the `ResultSet` object and the options hash map (as passed into various `next.jdbc` functions). They return an implementation of the protocols that is then used to build rows and the result set. Note that the `ResultSet` passed in is _mutable_ and is advanced from row to row by the SQL execution function, so each time `->row` is called, the underlying `ResultSet` object points at each new row in turn. By contrast, `->rs` (which is only called by `execute!`) is invoked _before_ the `ResultSet` is advanced to the first row.
The options hash map for any `next.jdbc` function can contain a `:builder-fn` key and the value is used as the row/result set builder function. The tests for `next.jdbc.result-set` include a [record-based builder function](https://github.com/seancorfield/next-jdbc/blob/master/test/next/jdbc/result_set_test.clj#L335-L353) as an example of how you can extend this to satisfy your needs.
The options hash map for any `next.jdbc` function can contain a `:builder-fn` key and the value is used as the row/result set builder function. The tests for `next.jdbc.result-set` include a [record-based builder function](https://github.com/seancorfield/next-jdbc/blob/develop/test/next/jdbc/result_set_test.clj#L335-L353) as an example of how you can extend this to satisfy your needs.
> Note: When `next.jdbc` cannot obtain a `ResultSet` object and returns `{:next.jdbc/count N}` instead, the builder function is not applied -- the `:builder-fn` option does not affect the shape of the result.

View file

@ -60,7 +60,7 @@ Examples:
;; socketTimeout via JDBC URL:
(def db-url (str "jdbc:sqlserver://localhost;user=sa;password=secret"
;; milliseconds:
";database=master;socketTimeout=10000"))
";database=model;socketTimeout=10000"))
;; loginTimeout via DataSource:
(def ds (jdbc/get-datasource db-spec))

View file

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>seancorfield</groupId>
<artifactId>next.jdbc</artifactId>
<version>1.0.462</version>
<version>1.0.475</version>
<name>next.jdbc</name>
<description>The next generation of clojure.java.jdbc: a new low-level Clojure wrapper for JDBC-based access to databases.</description>
<url>https://github.com/seancorfield/next-jdbc</url>
@ -22,7 +22,7 @@
<url>https://github.com/seancorfield/next-jdbc</url>
<connection>scm:git:git://github.com/seancorfield/next-jdbc.git</connection>
<developerConnection>scm:git:ssh://git@github.com/seancorfield/next-jdbc.git</developerConnection>
<tag>v1.0.462</tag>
<tag>v1.0.475</tag>
</scm>
<dependencies>
<dependency>

View file

@ -58,6 +58,7 @@
In addition, wherever a `PreparedStatement` is created, you may specify:
* `:return-keys` -- either `true` or a vector of key names to return."
(:require [next.jdbc.connection]
[next.jdbc.default-options :as opts]
[next.jdbc.prepare]
[next.jdbc.protocols :as p]
[next.jdbc.result-set]
@ -265,3 +266,10 @@
[[sym transactable opts] & body]
(let [con (vary-meta sym assoc :tag 'java.sql.Connection)]
`(transact ~transactable (^{:once true} fn* [~con] ~@body) ~(or opts {}))))
(defn with-options
"Given a connectable/transactable object and a set of (default) options
that should be used on all operations on that object, return a new
wrapper object that can be used in its place."
[connectable opts]
(opts/->DefaultOptions connectable opts))

View file

@ -4,7 +4,8 @@
"Standard implementations of `get-datasource` and `get-connection`.
Also provides `dbtypes` as a map of all known database types, and
the `->pool` function for creating pooled datasource objects."
the `->pool` and `component` functions for creating pooled datasource
objects."
(:require [clojure.java.data :as j]
[next.jdbc.protocols :as p])
(:import (java.sql Connection DriverManager)
@ -56,7 +57,7 @@
or:
`{com.acme/jdbc {:mvn/version \"1.2.3\"}} ; CLI/deps.edn`
`com.acme/jdbc {:mvn/version \"1.2.3\"} ; CLI/deps.edn`
Note: the `:classname` value can be a string or a vector of strings. If
a vector of strings is provided, an attempt will be made to load each

View file

@ -0,0 +1,42 @@
;; copyright (c) 2020 Sean Corfield, all rights reserved
(ns ^:no-doc next.jdbc.default-options
"Implementation of SQL transaction logic."
(:require [next.jdbc.protocols :as p]))
(defrecord DefaultOptions [connectable options])
(extend-protocol p/Sourceable
DefaultOptions
(get-datasource [this]
(p/get-datasource (:connectable this))))
(extend-protocol p/Connectable
DefaultOptions
(get-connection [this opts]
(p/get-connection (:connectable this)
(merge (:options this) opts))))
(extend-protocol p/Executable
DefaultOptions
(-execute [this sql-params opts]
(p/-execute (:connectable this) sql-params
(merge (:options this) opts)))
(-execute-one [this sql-params opts]
(p/-execute-one (:connectable this) sql-params
(merge (:options this) opts)))
(-execute-all [this sql-params opts]
(p/-execute-all (:connectable this) sql-params
(merge (:options this) opts))))
(extend-protocol p/Preparable
DefaultOptions
(prepare [this sql-params opts]
(p/prepare (:connectable this) sql-params
(merge (:options this) opts))))
(extend-protocol p/Transactable
DefaultOptions
(-transact [this body-fn opts]
(p/-transact (:connectable this) body-fn
(merge (:options this) opts))))

View file

@ -0,0 +1,9 @@
;; copyright (c) 2020 Sean Corfield, all rights reserved
(ns next.jdbc.default-options-test
"Stub test namespace for default options. Nothing can really be tested
at this level tho'..."
(:require [clojure.test :refer [deftest is testing]]
[next.jdbc.default-options :refer :all]))
(set! *warn-on-reflection* true)

View file

@ -21,12 +21,14 @@
(specs/instrument)
(deftest basic-tests
;; use ds-opts instead of (ds) anywhere you want default options applied:
(let [ds-opts (jdbc/with-options (ds) (default-options))]
(testing "plan"
(is (= "Apple"
(reduce (fn [_ row] (reduced (:name row)))
nil
(jdbc/plan
(ds)
ds-opts
["select * from fruit where appearance = ?" "red"])))))
(testing "execute-one!"
(is (nil? (jdbc/execute-one!
@ -34,34 +36,32 @@
["select * from fruit where appearance = ?" "neon-green"])))
(is (= "Apple" ((column :FRUIT/NAME)
(jdbc/execute-one!
(ds)
["select * from fruit where appearance = ?" "red"]
(default-options))))))
ds-opts
["select * from fruit where appearance = ?" "red"])))))
(testing "execute!"
(let [rs (jdbc/execute!
(ds)
ds-opts
["select * from fruit where appearance = ?" "neon-green"])]
(is (vector? rs))
(is (= [] rs)))
(let [rs (jdbc/execute!
(ds)
["select * from fruit where appearance = ?" "red"]
(default-options))]
ds-opts
["select * from fruit where appearance = ?" "red"])]
(is (= 1 (count rs)))
(is (= 1 ((column :FRUIT/ID) (first rs)))))
(let [rs (jdbc/execute!
(ds)
ds-opts
["select * from fruit order by id"]
(assoc (default-options) :builder-fn rs/as-maps))]
{:builder-fn rs/as-maps})]
(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)))))
(let [rs (jdbc/execute!
(ds)
ds-opts
["select * from fruit order by id"]
(assoc (default-options) :builder-fn rs/as-arrays))]
{:builder-fn rs/as-arrays})]
(is (every? vector? rs))
(is (= 5 (count rs)))
(is (every? #(= 5 (count %)) rs))
@ -74,13 +74,12 @@
(is (every? string? (map second (rest rs))))))
(testing "execute! with adapter"
(let [rs (jdbc/execute! ; test again, with adapter and lower columns
(ds)
ds-opts
["select * from fruit order by id"]
(assoc (default-options)
:builder-fn (rs/as-arrays-adapter
{:builder-fn (rs/as-arrays-adapter
rs/as-lower-arrays
(fn [^ResultSet rs _ ^Integer i]
(.getObject rs i)))))]
(.getObject rs i)))})]
(is (every? vector? rs))
(is (= 5 (count rs)))
(is (every? #(= 5 (count %)) rs))
@ -102,7 +101,7 @@
(is (= 1 ((column :ID) (first rs))))
(is (= 4 ((column :ID) (last rs)))))
(let [rs (jdbc/execute!
(ds)
ds-opts
["select * from fruit order by id"]
{:builder-fn rs/as-unqualified-arrays})]
(is (every? vector? rs))
@ -117,24 +116,25 @@
(is (every? string? (map second (rest rs))))))
(testing "execute! with :max-rows / :maxRows"
(let [rs (jdbc/execute!
(ds)
ds-opts
["select * from fruit order by id"]
(assoc (default-options) :max-rows 2))]
{:max-rows 2})]
(is (every? map? rs))
(is (every? meta rs))
(is (= 2 (count rs)))
(is (= 1 ((column :FRUIT/ID) (first rs))))
(is (= 2 ((column :FRUIT/ID) (last rs)))))
(let [rs (jdbc/execute!
(ds)
ds-opts
["select * from fruit order by id"]
(assoc (default-options) :statement {:maxRows 2}))]
{:statement {:maxRows 2}})]
(is (every? map? rs))
(is (every? meta rs))
(is (= 2 (count rs)))
(is (= 1 ((column :FRUIT/ID) (first rs))))
(is (= 2 ((column :FRUIT/ID) (last rs))))))
(is (= 2 ((column :FRUIT/ID) (last rs)))))))
(testing "prepare"
;; default options do not flow over get-connection
(let [rs (with-open [con (jdbc/get-connection (ds))
ps (jdbc/prepare
con
@ -146,6 +146,7 @@
(is (= 4 (count rs)))
(is (= 1 ((column :FRUIT/ID) (first rs))))
(is (= 4 ((column :FRUIT/ID) (last rs)))))
;; default options do not flow over get-connection
(let [rs (with-open [con (jdbc/get-connection (ds))
ps (jdbc/prepare
con
@ -157,6 +158,7 @@
(is (= 1 (count rs)))
(is (= 4 ((column :FRUIT/ID) (first rs))))))
(testing "statement"
;; default options do not flow over get-connection
(let [rs (with-open [con (jdbc/get-connection (ds))]
(jdbc/execute! (prep/statement con (default-options))
["select * from fruit order by id"]))]
@ -165,6 +167,7 @@
(is (= 4 (count rs)))
(is (= 1 ((column :FRUIT/ID) (first rs))))
(is (= 4 ((column :FRUIT/ID) (last rs)))))
;; default options do not flow over get-connection
(let [rs (with-open [con (jdbc/get-connection (ds))]
(jdbc/execute! (prep/statement con (default-options))
["select * from fruit where id = 4"]))]