Provide a way to force builder construction in plan

This still won't catch the empty result set or lack of result set cases 
(e.g., update count only).
This commit is contained in:
Sean Corfield 2019-11-17 12:30:32 -08:00
parent bebcf0af17
commit bb6eb02cbc
3 changed files with 27 additions and 1 deletions

View file

@ -7,6 +7,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: The following changes have been committed to the **master** branch since the 1.0.10 release:
* Add `next.jdbc.middleware` containing a `wrapper` for connectable objects that can offer default options, as well as four "hooks" for pre- and post-processing functions that make it easier to add logging and timing code to your `next.jdbc`-based application. * Add `next.jdbc.middleware` containing a `wrapper` for connectable objects that can offer default options, as well as four "hooks" for pre- and post-processing functions that make it easier to add logging and timing code to your `next.jdbc`-based application.
* Make the "mapified" result set object implement `clojure.lang.IDeref` so you can "force" the result set builder to be constructed so that the `:post-execute-fn` hook will run in middleware.
* Add testing against Microsoft SQL Server (run tests with environment variables `NEXT_JDBC_TEST_MSSQL=yes` and `MSSQL_SA_PASSWORD` set to your local -- `127.0.0.1:1433` -- SQL Server `sa` user password; assumes that it can create and drop `fruit` and `fruit_time` tables in the `model` database). * Add testing against Microsoft SQL Server (run tests with environment variables `NEXT_JDBC_TEST_MSSQL=yes` and `MSSQL_SA_PASSWORD` set to your local -- `127.0.0.1:1433` -- 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 -- `127.0.0.1:3306` -- MySQL `root` user password; assumes you have already created an empty database called `clojure_test`). * Add testing against MySQL (run tests with environment variables `NEXT_JDBC_TEST_MYSQL=yes` and `MYSQL_ROOT_PASSWORD` set to your local -- `127.0.0.1:3306` -- 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. * Bump several JDBC driver versions for up-to-date testing.

View file

@ -63,6 +63,28 @@ As you can see, both `:pre-process-fn` and `:post-process-fn` can return updated
Any of the hook functions may execute side-effects (such as logging) but must still return the expected data. Any of the hook functions may execute side-effects (such as logging) but must still return the expected data.
## Middleware and `plan`
Because `next.jdbc/plan` tries to avoid realizing a result set, it is possible to perform reductions that do not even cause the result set builder to be constructed -- the `:post-execute-fn` hook will not executed in such cases. For example:
```clojure
user=> (into [] (map :name) ; does not construct the builder!
(jdbc/plan db-spec ["select * from fruit"]))
["Apple" "Banana" "Peach" "Orange"]
```
You can force the result set builder to be constructed by calling `deref` on any row:
```clojure
user=> (into [] (map (comp :name deref))
(jdbc/plan db-spec ["select * from fruit"]))
["Apple" "Banana" "Peach" "Orange"]
```
That will ensure that the result set builder _is_ constructed and it will execute the `:post-execute-fn` hook, but it will not cause rows (or the overall result set) to be realized. Thus, the only overhead of calling `deref` on each row is the one-off cost of constructing the result set builder for the first row, and the cost of derefencing a realized delay object for each row (and throwing that value away).
*Note: If your SQL operation produces no rows, or no `ResultSet` at all (only an update count), then there is no way to force the result set builder to be constructed and no way for the `:post-execute-fn` hook to be executed.*
## Examples of Middleware Usage ## Examples of Middleware Usage
The usage for providing default options should be clear from the overview above The usage for providing default options should be clear from the overview above

View file

@ -462,7 +462,10 @@
clojure.lang.IDeref clojure.lang.IDeref
(deref [this] (deref [this]
(deref builder)) ;; force the builder to be created but return the row
;; without actually building anything
(deref builder)
this)
;; from java.lang.Object: ;; from java.lang.Object:
(toString [_] (toString [_]