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:
* 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 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.

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.
## 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
The usage for providing default options should be clear from the overview above

View file

@ -462,7 +462,10 @@
clojure.lang.IDeref
(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:
(toString [_]