fixes #282 by tracking raw Connection objects for TXs.
this no longer checks TX nesting for DataSource-based TXs, but instead uses the Connection-based implementation directly. raw Connection objects are tracked in a dynamic set. thanks to [mbezjak](https://github.com/mbezjak) for the core of the implementation. Signed-off-by: Sean Corfield <sean@corfield.org>
This commit is contained in:
parent
a75468105f
commit
b0a640a101
4 changed files with 43 additions and 35 deletions
|
|
@ -4,6 +4,7 @@ Only accretive/fixative changes will be made from now on.
|
||||||
|
|
||||||
* 1.3.next in progress
|
* 1.3.next in progress
|
||||||
* Fix [#287](https://github.com/seancorfield/next-jdbc/issues/287) by merging user-supplied options over `:return-keys true`.
|
* Fix [#287](https://github.com/seancorfield/next-jdbc/issues/287) by merging user-supplied options over `:return-keys true`.
|
||||||
|
* Fix [#282](https://github.com/seancorfield/next-jdbc/issues/282) by tracking raw `Connection` objects for active TXs, which relaxes several of the conditions around nested transactions.
|
||||||
|
|
||||||
* 1.3.955 -- 2024-10-06
|
* 1.3.955 -- 2024-10-06
|
||||||
* Address [#285](https://github.com/seancorfield/next-jdbc/issues/285) by setting the default Clojure version to the earliest supported (1.10.3) to give a better hint to users.
|
* Address [#285](https://github.com/seancorfield/next-jdbc/issues/285) by setting the default Clojure version to the earliest supported (1.10.3) to give a better hint to users.
|
||||||
|
|
|
||||||
|
|
@ -447,12 +447,19 @@
|
||||||
"Returns true if `next.jdbc` has a currently active transaction in the
|
"Returns true if `next.jdbc` has a currently active transaction in the
|
||||||
current thread, else false.
|
current thread, else false.
|
||||||
|
|
||||||
|
With no arguments, tells you if any transaction is currently active.
|
||||||
|
|
||||||
|
With a `Connection` argument, tells you if a transaction is currently
|
||||||
|
active on that specific connection.
|
||||||
|
|
||||||
Note: transactions are a convention of operations on a `Connection` so
|
Note: transactions are a convention of operations on a `Connection` so
|
||||||
this predicate only reflects `next.jdbc/transact` and `next.jdbc/with-transaction`
|
this predicate only reflects `next.jdbc/transact` and `next.jdbc/with-transaction`
|
||||||
operations -- it does not reflect any other operations on a `Connection`,
|
operations -- it does not reflect any other operations on a `Connection`,
|
||||||
performed via JDBC interop directly."
|
performed via JDBC interop directly."
|
||||||
[]
|
([]
|
||||||
@#'tx/*active-tx*)
|
(boolean (seq @#'tx/*active-tx*)))
|
||||||
|
([con]
|
||||||
|
(contains? @#'tx/*active-tx* con)))
|
||||||
|
|
||||||
(defn with-options
|
(defn with-options
|
||||||
"Given a connectable/transactable object and a set of (default) options
|
"Given a connectable/transactable object and a set of (default) options
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
:allow)
|
:allow)
|
||||||
|
|
||||||
(defonce ^:private ^:dynamic ^{:doc "Used to detect nested transactions."}
|
(defonce ^:private ^:dynamic ^{:doc "Used to detect nested transactions."}
|
||||||
*active-tx* false)
|
*active-tx* #{})
|
||||||
|
|
||||||
(def ^:private isolation-levels
|
(def ^:private isolation-levels
|
||||||
"Transaction isolation levels."
|
"Transaction isolation levels."
|
||||||
|
|
@ -112,43 +112,37 @@
|
||||||
(.setReadOnly con old-readonly)
|
(.setReadOnly con old-readonly)
|
||||||
(catch Exception _))))))))
|
(catch Exception _))))))))
|
||||||
|
|
||||||
|
(defn- raw-connection ^Connection [^Connection con]
|
||||||
|
(if (.isWrapperFor con Connection)
|
||||||
|
(.unwrap con Connection)
|
||||||
|
con))
|
||||||
|
|
||||||
(extend-protocol p/Transactable
|
(extend-protocol p/Transactable
|
||||||
java.sql.Connection
|
java.sql.Connection
|
||||||
(-transact [this body-fn opts]
|
(-transact [this body-fn opts]
|
||||||
(cond
|
(let [raw (raw-connection this)]
|
||||||
(and (not *active-tx*) (= :ignore *nested-tx*))
|
(cond
|
||||||
;; #245 do not lock when in c.j.j compatibility mode:
|
(and (not (contains? *active-tx* raw)) (= :ignore *nested-tx*))
|
||||||
(binding [*active-tx* true]
|
;; #245 do not lock when in c.j.j compatibility mode:
|
||||||
(transact* this body-fn opts))
|
(binding [*active-tx* (conj *active-tx* raw)]
|
||||||
(or (not *active-tx*) (= :allow *nested-tx*))
|
(transact* this body-fn opts))
|
||||||
(locking this
|
(or (not (contains? *active-tx* raw)) (= :allow *nested-tx*))
|
||||||
(binding [*active-tx* true]
|
(locking this
|
||||||
(transact* this body-fn opts)))
|
(binding [*active-tx* (conj *active-tx* raw)]
|
||||||
(= :ignore *nested-tx*)
|
(transact* this body-fn opts)))
|
||||||
(body-fn this)
|
(= :ignore *nested-tx*)
|
||||||
(= :prohibit *nested-tx*)
|
(body-fn this)
|
||||||
(throw (IllegalStateException. "Nested transactions are prohibited"))
|
(= :prohibit *nested-tx*)
|
||||||
:else
|
(throw (IllegalStateException. "Nested transactions are prohibited"))
|
||||||
(throw (IllegalArgumentException.
|
:else
|
||||||
(str "*nested-tx* ("
|
(throw (IllegalArgumentException.
|
||||||
*nested-tx*
|
(str "*nested-tx* ("
|
||||||
") was not :allow, :ignore, or :prohibit")))))
|
*nested-tx*
|
||||||
|
") was not :allow, :ignore, or :prohibit"))))))
|
||||||
javax.sql.DataSource
|
javax.sql.DataSource
|
||||||
(-transact [this body-fn opts]
|
(-transact [this body-fn opts]
|
||||||
(cond (or (not *active-tx*) (= :allow *nested-tx*))
|
(with-open [con (p/get-connection this opts)]
|
||||||
(binding [*active-tx* true]
|
(p/-transact con body-fn opts)))
|
||||||
(with-open [con (p/get-connection this opts)]
|
|
||||||
(transact* con body-fn opts)))
|
|
||||||
(= :ignore *nested-tx*)
|
|
||||||
(with-open [con (p/get-connection this opts)]
|
|
||||||
(body-fn con))
|
|
||||||
(= :prohibit *nested-tx*)
|
|
||||||
(throw (IllegalStateException. "Nested transactions are prohibited"))
|
|
||||||
:else
|
|
||||||
(throw (IllegalArgumentException.
|
|
||||||
(str "*nested-tx* ("
|
|
||||||
*nested-tx*
|
|
||||||
") was not :allow, :ignore, or :prohibit")))))
|
|
||||||
Object
|
Object
|
||||||
(-transact [this body-fn opts]
|
(-transact [this body-fn opts]
|
||||||
(p/-transact (p/get-datasource this) body-fn opts)))
|
(p/-transact (p/get-datasource this) body-fn opts)))
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,7 @@ VALUES ('Pear', 'green', 49, 47)
|
||||||
(is (= [{:next.jdbc/update-count 1}]
|
(is (= [{:next.jdbc/update-count 1}]
|
||||||
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
||||||
(is (jdbc/active-tx?) "should be in a transaction")
|
(is (jdbc/active-tx?) "should be in a transaction")
|
||||||
|
(is (jdbc/active-tx? t) "connection should be in a transaction")
|
||||||
(jdbc/execute! t ["
|
(jdbc/execute! t ["
|
||||||
INSERT INTO fruit (name, appearance, cost, grade)
|
INSERT INTO fruit (name, appearance, cost, grade)
|
||||||
VALUES ('Pear', 'green', 49, 47)
|
VALUES ('Pear', 'green', 49, 47)
|
||||||
|
|
@ -244,6 +245,7 @@ VALUES ('Pear', 'green', 49, 47)
|
||||||
(is (= [{:next.jdbc/update-count 1}]
|
(is (= [{:next.jdbc/update-count 1}]
|
||||||
(jdbc/with-transaction [t con {:rollback-only true}]
|
(jdbc/with-transaction [t con {:rollback-only true}]
|
||||||
(is (jdbc/active-tx?) "should be in a transaction")
|
(is (jdbc/active-tx?) "should be in a transaction")
|
||||||
|
(is (jdbc/active-tx? t) "connection should be in a transaction")
|
||||||
(jdbc/execute! t ["
|
(jdbc/execute! t ["
|
||||||
INSERT INTO fruit (name, appearance, cost, grade)
|
INSERT INTO fruit (name, appearance, cost, grade)
|
||||||
VALUES ('Pear', 'green', 49, 47)
|
VALUES ('Pear', 'green', 49, 47)
|
||||||
|
|
@ -258,6 +260,7 @@ INSERT INTO fruit (name, appearance, cost, grade)
|
||||||
VALUES ('Pear', 'green', 49, 47)
|
VALUES ('Pear', 'green', 49, 47)
|
||||||
"])
|
"])
|
||||||
(is (jdbc/active-tx?) "should be in a transaction")
|
(is (jdbc/active-tx?) "should be in a transaction")
|
||||||
|
(is (jdbc/active-tx? t) "connection should be in a transaction")
|
||||||
(throw (ex-info "abort" {})))))
|
(throw (ex-info "abort" {})))))
|
||||||
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"]))))
|
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"]))))
|
||||||
(is (not (jdbc/active-tx?)) "should not be in a transaction")
|
(is (not (jdbc/active-tx?)) "should not be in a transaction")
|
||||||
|
|
@ -270,6 +273,7 @@ INSERT INTO fruit (name, appearance, cost, grade)
|
||||||
VALUES ('Pear', 'green', 49, 47)
|
VALUES ('Pear', 'green', 49, 47)
|
||||||
"])
|
"])
|
||||||
(is (jdbc/active-tx?) "should be in a transaction")
|
(is (jdbc/active-tx?) "should be in a transaction")
|
||||||
|
(is (jdbc/active-tx? t) "connection should be in a transaction")
|
||||||
(throw (ex-info "abort" {})))))
|
(throw (ex-info "abort" {})))))
|
||||||
(is (= 4 (count (jdbc/execute! con ["select * from fruit"]))))
|
(is (= 4 (count (jdbc/execute! con ["select * from fruit"]))))
|
||||||
(is (= ac (.getAutoCommit con))))))
|
(is (= ac (.getAutoCommit con))))))
|
||||||
|
|
@ -283,6 +287,7 @@ VALUES ('Pear', 'green', 49, 47)
|
||||||
(.rollback t)
|
(.rollback t)
|
||||||
;; still in a next.jdbc TX even tho' we rolled back!
|
;; still in a next.jdbc TX even tho' we rolled back!
|
||||||
(is (jdbc/active-tx?) "should be in a transaction")
|
(is (jdbc/active-tx?) "should be in a transaction")
|
||||||
|
(is (jdbc/active-tx? t) "connection should be in a transaction")
|
||||||
result))))
|
result))))
|
||||||
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"]))))
|
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"]))))
|
||||||
(is (not (jdbc/active-tx?)) "should not be in a transaction")
|
(is (not (jdbc/active-tx?)) "should not be in a transaction")
|
||||||
|
|
@ -309,6 +314,7 @@ VALUES ('Pear', 'green', 49, 47)
|
||||||
(.rollback t save-point)
|
(.rollback t save-point)
|
||||||
;; still in a next.jdbc TX even tho' we rolled back to a save point!
|
;; still in a next.jdbc TX even tho' we rolled back to a save point!
|
||||||
(is (jdbc/active-tx?) "should be in a transaction")
|
(is (jdbc/active-tx?) "should be in a transaction")
|
||||||
|
(is (jdbc/active-tx? t) "connection should be in a transaction")
|
||||||
result))))
|
result))))
|
||||||
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"]))))
|
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"]))))
|
||||||
(is (not (jdbc/active-tx?)) "should not be in a transaction")
|
(is (not (jdbc/active-tx?)) "should not be in a transaction")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue