Skip to content

Commit

Permalink
add on-connection+options #256
Browse files Browse the repository at this point in the history
  • Loading branch information
seancorfield committed Aug 10, 2023
1 parent cd214cb commit 9ea5b17
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 15 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Only accretive/fixative changes will be made from now on.

* 1.3.next in progress
* Address [#256](https://github.com/seancorfield/next-jdbc/issues/256) by adding `with-transaction+options`. Documentation TBD.
* Address [#256](https://github.com/seancorfield/next-jdbc/issues/256) by adding `with-transaction+options` and `on-connection+options`. Documentation TBD.

* 1.3.883 -- 2023-06-25
* Address [#254](https://github.com/seancorfield/next-jdbc/issues/254) by adding `next.jdbc/active-tx?` and adding more explanation to [**Transactions**](https://cljdoc.org/d/com.github.seancorfield/next.jdbc/CURRENT/doc/getting-started/transactions) about the conventions behind transactions and the limitations of thread-local tracking of active transactions in `next.jdbc`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
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}}
:lint-as {next.jdbc/on-connection clojure.core/with-open
next.jdbc/on-connection+options clojure.core/with-open}}
52 changes: 43 additions & 9 deletions src/next/jdbc.clj
Original file line number Diff line number Diff line change
Expand Up @@ -360,16 +360,50 @@
Otherwise, creates a new `Connection` object from the connectable,
executes the body, and automatically closes it for you."
[[sym connectable] & body]
(let [con-sym (vary-meta sym assoc :tag 'java.sql.Connection)
con-obj connectable]
`(cond (instance? java.sql.Connection ~con-obj)
((^{:once true} fn* [~con-sym] ~@body) ~con-obj)
(and (satisfies? p/Connectable ~con-obj)
(instance? java.sql.Connection (:connectable ~con-obj)))
((^{:once true} fn* [~con-sym] ~@body) (:connectable ~con-obj))
(let [con-sym (vary-meta sym assoc :tag 'java.sql.Connection)]
`(let [con-obj# ~connectable]
(cond (instance? java.sql.Connection con-obj#)
((^{:once true} fn* [~con-sym] ~@body) con-obj#)
(and (satisfies? p/Connectable con-obj#)
(instance? java.sql.Connection (:connectable con-obj#)))
((^{:once true} fn* [~con-sym] ~@body) (:connectable con-obj#))
:else
(with-open [con# (get-connection con-obj#)]
((^{:once true} fn* [~con-sym] ~@body) con#))))))

(defmacro on-connection+options
"Given a connectable object, assumed to be wrapped with options, gets
a connection, rewraps it with those options, and binds it to `sym`,
then executes the `body` in that context.
This allows you to write generic, **wrapped** connectable code without
needing to know the exact type of an incoming datasource:
```clojure
(on-connection+options [conn datasource]
(execute! conn some-insert-sql)
(execute! conn some-update-sql))
```
If passed a `Connection` then that `Connection` is used as-is.
If passed a `Connectable` that wraps a `Connection`, then that
`Connectable` is used as-is.
Otherwise, creates a new `Connection` object from the connectable,
wraps that with options, executes the body, and automatically closes
the new `Connection` for you."
[[sym connectable] & body]
`(let [con-obj# ~connectable]
(cond (instance? java.sql.Connection con-obj#)
((^{:once true} fn* [~sym] ~@body) con-obj#)
(and (satisfies? p/Connectable con-obj#)
(instance? java.sql.Connection (:connectable con-obj#)))
((^{:once true} fn* [~sym] ~@body) con-obj#)
:else
(with-open [con# (get-connection ~con-obj)]
((^{:once true} fn* [~con-sym] ~@body) con#)))))
(with-open [con# (get-connection con-obj#)]
((^{:once true} fn* [~sym] ~@body)
(with-options con# (:options con-obj# {})))))))

(defn transact
"Given a transactable object and a function (taking a `Connection`),
Expand Down
46 changes: 42 additions & 4 deletions test/next/jdbc_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -944,11 +944,49 @@ INSERT INTO fruit (name, appearance) VALUES (?,?)
(deftest issue-204
(testing "against a Connection"
(is (seq (with-open [con (jdbc/get-connection (ds))]
(jdbc/on-connection [x con] (jdbc/execute! x ["select * from fruit"]))))))
(jdbc/on-connection
[x con]
(jdbc/execute! x ["select * from fruit"]))))))
(testing "against a wrapped Connection"
(is (seq (with-open [con (jdbc/get-connection (ds))]
(jdbc/on-connection [x (jdbc/with-options con {})] (jdbc/execute! x ["select * from fruit"]))))))
(jdbc/on-connection
[x (jdbc/with-options con {})]
(jdbc/execute! x ["select * from fruit"]))))))
(testing "against a wrapped Datasource"
(is (seq (jdbc/on-connection [x (jdbc/with-options (ds) {})] (jdbc/execute! x ["select * from fruit"])))))
(is (seq (jdbc/on-connection
[x (jdbc/with-options (ds) {})]
(jdbc/execute! x ["select * from fruit"])))))
(testing "against a Datasource"
(is (seq (jdbc/on-connection [x (ds)] (jdbc/execute! x ["select * from fruit"]))))))
(is (seq (jdbc/on-connection
[x (ds)]
(jdbc/execute! x ["select * from fruit"]))))))

(deftest issue-256
(testing "against a Connection"
(is (seq (with-open [con (jdbc/get-connection (ds))]
(jdbc/on-connection+options
[x con] ; raw connection stays raw
(is (instance? java.sql.Connection x))
(jdbc/execute! x ["select * from fruit"]))))))
(testing "against a wrapped Connection"
(is (seq (with-open [con (jdbc/get-connection (ds))]
(jdbc/on-connection+options
[x (jdbc/with-options con {:test-option 42})]
;; ensure we get the same wrapped connection
(is (instance? java.sql.Connection (:connectable x)))
(is (= {:test-option 42} (:options x)))
(jdbc/execute! x ["select * from fruit"]))))))
(testing "against a wrapped Datasource"
(is (seq (jdbc/on-connection+options
[x (jdbc/with-options (ds) {:test-option 42})]
;; ensure we get a wrapped connection
(is (instance? java.sql.Connection (:connectable x)))
(is (= {:test-option 42} (:options x)))
(jdbc/execute! x ["select * from fruit"])))))
(testing "against a Datasource"
(is (seq (jdbc/on-connection+options
[x (ds)] ; unwrapped datasource has no options
;; ensure we get a wrapped connection (empty options)
(is (instance? java.sql.Connection (:connectable x)))
(is (= {} (:options x)))
(jdbc/execute! x ["select * from fruit"]))))))

0 comments on commit 9ea5b17

Please sign in to comment.