diff --git a/CHANGELOG.md b/CHANGELOG.md
index cef448a..dcd5c62 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,4 @@
# Change Log
-2019-04-21 -- 1.0.0-alpha8 -- Initial publicly announced release.
+* 2019-04-22 -- 1.0.0-alpha9 -- Fix #14 by respecting `:gen-fn` in `execute-one` for `PreparedStatement`.
+* 2019-04-21 -- 1.0.0-alpha8 -- Initial publicly announced release.
diff --git a/doc/getting-started.md b/doc/getting-started.md
index 0d46134..e9714d6 100644
--- a/doc/getting-started.md
+++ b/doc/getting-started.md
@@ -9,12 +9,12 @@ 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.0-alpha8"}}
+{seancorfield/next.jdbc {:mvn/version "1.0.0-alpha9"}}
```
for `deps.edn` or:
```clojure
-[seancorfield/next.jdbc "1.0.0-alpha8"]
+[seancorfield/next.jdbc "1.0.0-alpha9"]
```
for `project.clj` or `build.boot`.
@@ -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.0"}
- seancorfield/next.jdbc {:mvn/version "1.0.0-alpha8"}
+ seancorfield/next.jdbc {:mvn/version "1.0.0-alpha9"}
com.h2database/h2 {:mvn/version "1.4.197"}}}
```
diff --git a/doc/migration-from-clojure-java-jdbc.md b/doc/migration-from-clojure-java-jdbc.md
index 8103e9a..801de05 100644
--- a/doc/migration-from-clojure-java-jdbc.md
+++ b/doc/migration-from-clojure-java-jdbc.md
@@ -4,7 +4,7 @@ This page attempts to list all of the differences between [clojure.java.jdbc](ht
## Conceptually
-`clojure.java.jdbc` focuses heavily on a `db-spec` hash map to describe the various ways of interacting with the database and grew from very imperative origins that expose a lot of the JDBC API (multiple type of SQL execution, some operations returned hash maps, others update counts as integers, etc).
+`clojure.java.jdbc` focuses heavily on a `db-spec` hash map to describe the various ways of interacting with the database and grew from very imperative origins that expose a lot of the JDBC API (multiple types of SQL execution, some operations returned hash maps, others update counts as integers, etc).
`next.jdbc` focuses on using protocols and native Java JDBC types where possible (for performance and simplicity) and strives to present a more modern Clojure API with namespace-qualified keywords in hash maps, reducible SQL operations as part of the primary API, and a streamlined set of SQL execution primitives. Execution always returns a hash map (for one result) or a vector of hash maps (for multiple results) -- even update counts are returned as if they were result sets.
diff --git a/pom.xml b/pom.xml
index 471a56a..ca6f799 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
seancorfield
next.jdbc
- 1.0.0-alpha8
+ 1.0.0-alpha9
next.jdbc
The next generation of clojure.java.jdbc: a new low-level Clojure wrapper for JDBC-based access to databases.
diff --git a/src/next/jdbc/prepare.clj b/src/next/jdbc/prepare.clj
index 7c77e84..33e5a51 100644
--- a/src/next/jdbc/prepare.clj
+++ b/src/next/jdbc/prepare.clj
@@ -20,8 +20,9 @@
(defprotocol SettableParameter :extend-via-metadata true
"Protocol for setting SQL parameters in statement objects, which
can convert from Clojure values. The default implementation just
- calls `.setObject` on the parameter value. It can be extended to use other
- methods of `PreparedStatement` to convert and set parameter values."
+ calls `.setObject` on the parameter value. It can be extended to
+ use other methods of `PreparedStatement` to convert and set parameter
+ values. Extension via metadata is supported."
(set-parameter [val stmt ix]
"Convert a Clojure value into a SQL value and store it as the ix'th
parameter in the given SQL statement object."))
diff --git a/src/next/jdbc/protocols.clj b/src/next/jdbc/protocols.clj
index c3ef2b5..0aa83a1 100644
--- a/src/next/jdbc/protocols.clj
+++ b/src/next/jdbc/protocols.clj
@@ -3,59 +3,61 @@
(ns next.jdbc.protocols
"This is the extensible core of the next generation java.jdbc library.
- `Sourceable` protocol:
- `get-datasource` -- turn something into a `javax.sql.DataSource`; implementations
- are provided for strings, hash maps (`db-spec` structures), and also a
- `DataSource` (which just returns itself).
-
- `Connectable` protocol:
- `get-connection` -- create a new JDBC connection that should be closed when you
- are finished with it; implementations are provided for `DataSource`,
- `PreparedStatement`, and `Object`, on the assumption that an `Object`
- can possibly be turned into a `DataSource`.
-
- `Executable` protocol:
- `-execute` -- given SQL and parameters, produce a 'reducible' that, when
- reduced, executes the SQL and produces a `ResultSet` that can be processed;
- implementations are provided for `Connection`, `DataSource`,
- `PreparedStatement`, and `Object` (on the assumption that an `Object` can be
- turned into a `DataSource` and therefore used to get a `Connection`).
-
- `-execute-one` -- given SQL and parameters, executes the SQL and produces
- the first row of the `ResultSet` as a datafiable hash map (by default);
- implementations are provided for `Connection`, `DataSource`,
- `PreparedStatement`, and `Object` (on the assumption that an `Object` can be
- turned into a `DataSource` and therefore used to get a `Connection`).
-
- `-execute-all` -- given SQL and parameters, executes the SQL and produces
- either a vector of datafiable hash maps from the `ResultSet` (default)
- or a vector of column names followed by vectors of row values;
- implementations are provided for `Connection`, `DataSource`,
- `PreparedStatement`, and `Object` (on the assumption that an `Object` can be
- turned into a `DataSource` and therefore used to get a `Connection`).
-
- `Preparable` protocol:
- `prepare` -- given SQL and parameters, produce a `PreparedStatement` that can
- be executed (by -execute above); implementation is provided for
- `Connection` only.
-
- `Transactable` protocol:
- `-transact` -- given a function (presumably containing SQL operations),
- run the function inside a SQL transaction; implementations are provided
- for `Connection`, `DataSource`, and `Object` (on the assumption that an
- `Object` can be turned into a `DataSource`).")
+ `Sourceable` -- for producing `javax.sql.DataSource` objects,
+ `Connectable` -- for producing new `java.sql.Connection` objects,
+ `Executable` -- for executing SQL operations,
+ `Preparable` -- for producing new `java.sql.PreparedStatement` objects,
+ `Transactable` -- for executing SQL operations transactionally.")
(set! *warn-on-reflection* true)
(defprotocol Sourceable :extend-via-metadata true
- (get-datasource ^javax.sql.DataSource [this]))
+ "Protocol for producing a `javax.sql.DataSource`.
+
+ Implementations are provided for strings, hash maps (`db-spec` structures),
+ and also a `DataSource` (which just returns itself).
+
+ Extension via metadata is supported."
+ (get-datasource ^javax.sql.DataSource [this]
+ "Produce a `javax.sql.DataSource`."))
+
(defprotocol Connectable
- (get-connection ^java.lang.AutoCloseable [this opts]))
+ "Protocol for producing a new JDBC connection that should be closed when you
+ are finished with it.
+
+ Implementations are provided for `DataSource`, `PreparedStatement`, and
+ `Object`, on the assumption that an `Object` can be turned into a `DataSource`."
+ (get-connection ^java.lang.AutoCloseable [this opts]
+ "Produce a new `java.sql.Connection` for use with `with-open`."))
+
(defprotocol Executable
- (-execute ^clojure.lang.IReduceInit [this sql-params opts])
- (-execute-one [this sql-params opts])
- (-execute-all [this sql-params opts]))
+ "Protocol for executing SQL operations.
+
+ Implementations are provided for `Connection`, `DataSource`,
+ `PreparedStatement`, and `Object`, on the assumption that an `Object` can be
+ turned into a `DataSource` and therefore used to get a `Connection`."
+ (-execute ^clojure.lang.IReduceInit [this sql-params opts]
+ "Produce a 'reducible' that, when reduced, executes the SQL and
+ processes the rows of the `ResultSet` directly.")
+ (-execute-one [this sql-params opts]
+ "Executes the SQL and produces the first row of the `ResultSet`
+ as a fully-realized, datafiable hash map (by default).")
+ (-execute-all [this sql-params opts]
+ "Executes the SQL and produces (by default) a vector of
+ fully-realized, datafiable hash maps from the `ResultSet`."))
+
(defprotocol Preparable
- (prepare ^java.sql.PreparedStatement [this sql-params opts]))
+ "Protocol for producing a new `java.sql.PreparedStatement` that should
+ be closed after use. Can be used by `Executable` functions.
+
+ Implementation is provided for `Connection` only."
+ (prepare ^java.sql.PreparedStatement [this sql-params opts]
+ "Produce a new `java.sql.PreparedStatement` for use with `with-open`."))
+
(defprotocol Transactable
- (-transact [this body-fn opts]))
+ "Protocol for running SQL operations in a transaction.
+
+ Implementations are provided for `Connection`, `DataSource`, and `Object`
+ (on the assumption that an `Object` can be turned into a `DataSource`)."
+ (-transact [this body-fn opts]
+ "Run the `body-fn` inside a transaction."))
diff --git a/src/next/jdbc/result_set.clj b/src/next/jdbc/result_set.clj
index e47febf..6fa2886 100644
--- a/src/next/jdbc/result_set.clj
+++ b/src/next/jdbc/result_set.clj
@@ -76,30 +76,30 @@
(read-column-by-index [_1 _2 _3] nil))
(defprotocol RowBuilder
- "Protocol for building rows in various representations:
- `->row` -- called once per row to create the basis of each row
- `column-count` -- return the number of columns in each row
- `with-column` -- called with the row and the index of the column to be added;
- this is expected to read the column value from the `ResultSet`!
- `row!` -- called once per row to finalize each row once it is complete
+ "Protocol for building rows in various representations.
The default implementation for building hash maps: `MapResultSetBuilder`"
- (->row [_])
- (column-count [_])
- (with-column [_ row i])
- (row! [_ row]))
+ (->row [_]
+ "Called once per row to create the basis of each row.")
+ (column-count [_]
+ "Return the number of columns in each row.")
+ (with-column [_ row i]
+ "Called with the row and the index of the column to be added;
+ this is expected to read the column value from the `ResultSet`!")
+ (row! [_ row]
+ "Called once per row to finalize each row once it is complete."))
(defprotocol ResultSetBuilder
- "Protocol for building result sets in various representations:
- `->rs` -- called to create the basis of the result set
- `with-row` -- called with the result set and the row to be added
- `rs!` -- called to finalize the result set once it is complete
+ "Protocol for building result sets in various representations.
- Default implementations for building vectors of hash maps and vectors
- of column names and row values: `MapResultSetBuilder` & `ArrayResultSetBuilder`"
- (->rs [_])
- (with-row [_ rs row])
- (rs! [_ rs]))
+ Default implementations for building vectors of hash maps and vectors of
+ column names and row values: `MapResultSetBuilder` & `ArrayResultSetBuilder`"
+ (->rs [_]
+ "Called to create the basis of the result set.")
+ (with-row [_ rs row]
+ "Called with the result set and the row to be added.")
+ (rs! [_ rs]
+ "Called to finalize the result set once it is complete."))
(defrecord MapResultSetBuilder [^ResultSet rs rsmeta cols]
RowBuilder
@@ -199,9 +199,17 @@
(declare navize-row)
(defprotocol DatafiableRow
- "Given a connectable object, return a function that knows how to turn a row
- into a datafiable object that can be `nav`igated."
- (datafiable-row [this connectable opts]))
+ "Protocol for making rows datafiable and therefore navigable.
+
+ The default implementation just adds metadata so that `datafy` can be
+ called on the row, which will produce something that `nav` can be called
+ on, to lazily navigate through foreign key relationships into other tables.
+
+ If `datafiable-row` is called when reducing the result set produced by
+ `next.jdbc/reducible!`, the row is fully-realized from the `ResultSet`
+ first."
+ (datafiable-row [this connectable opts]
+ "Produce a datafiable representation of a row from a `ResultSet`."))
(defn- row-builder
"Given a `RowBuilder` -- a row materialization strategy -- produce a fully
@@ -396,9 +404,11 @@
(reduce-stmt this f init (assoc opts :return-keys true)))))
(-execute-one [this _ opts]
(if-let [rs (stmt->result-set this (assoc opts :return-keys true))]
- (when (.next rs)
- (datafiable-row (row-builder (as-maps rs opts))
- (.getConnection this) opts))
+ (let [gen-fn (get opts :gen-fn as-maps)
+ gen (gen-fn rs opts)]
+ (when (.next rs)
+ (datafiable-row (row-builder gen)
+ (.getConnection this) opts)))
{:next.jdbc/update-count (.getUpdateCount this)}))
(-execute-all [this _ opts]
(if-let [rs (stmt->result-set this opts)]