Optimizations

Strip options from datasource.
Implement full prepared statement.
Lift options handling.
This commit is contained in:
Sean Corfield 2019-01-26 01:35:31 -08:00
parent a1d9d869d6
commit 8d9289bd14

View file

@ -35,13 +35,14 @@
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
(defprotocol Sourceable (defprotocol Sourceable
(get-datasource ^DataSource [this opts])) (get-datasource ^DataSource [this]))
(defprotocol Connectable (defprotocol Connectable
(get-connection ^AutoCloseable [this])) (get-connection ^AutoCloseable [this]))
(defprotocol Executable (defprotocol Executable
(-execute ^clojure.lang.IReduceInit [this sql-params opts])) (-execute ^clojure.lang.IReduceInit [this sql-params opts]))
(defprotocol Preparable (defprotocol Preparable
(prepare ^PreparedStatement [this sql-params opts])) (prepare ^PreparedStatement [this sql-params opts])
(prepare-fn ^PreparedStatement [this sql-params factory]))
(defn execute! (defn execute!
"General SQL execution function. "General SQL execution function.
@ -62,12 +63,101 @@
(recur more (inc i))))) (recur more (inc i)))))
ps) ps)
(def ^{:private true
:doc "Map friendly :concurrency values to ResultSet constants."}
result-set-concurrency
{:read-only ResultSet/CONCUR_READ_ONLY
:updatable ResultSet/CONCUR_UPDATABLE})
(def ^{:private true
:doc "Map friendly :cursors values to ResultSet constants."}
result-set-holdability
{:hold ResultSet/HOLD_CURSORS_OVER_COMMIT
:close ResultSet/CLOSE_CURSORS_AT_COMMIT})
(def ^{:private true
:doc "Map friendly :type values to ResultSet constants."}
result-set-type
{:forward-only ResultSet/TYPE_FORWARD_ONLY
:scroll-insensitive ResultSet/TYPE_SCROLL_INSENSITIVE
:scroll-sensitive ResultSet/TYPE_SCROLL_SENSITIVE})
(defn- ^{:tag (class (into-array String []))} string-array
[return-keys]
(into-array String return-keys))
(defn- pre-prepare*
"Given a some options, return a function that will accept a connection and a
SQL string and parameters, and return a PreparedStatement representing that."
[{:keys [return-keys result-type concurrency cursors
fetch-size max-rows timeout]}]
(cond->
(cond
return-keys
(do
(when (or result-type concurrency cursors)
(throw (IllegalArgumentException.
(str ":concurrency, :cursors, and :result-type "
"may not be specified with :return-keys."))))
(if (vector? return-keys)
(let [key-names (string-array return-keys)]
(fn [^Connection con ^String sql]
(try
(try
(.prepareStatement con sql key-names)
(catch Exception _
;; assume it is unsupported and try regular generated keys:
(.prepareStatement con sql java.sql.Statement/RETURN_GENERATED_KEYS)))
(catch Exception _
;; assume it is unsupported and try basic PreparedStatement:
(.prepareStatement con sql)))))
(fn [^Connection con ^String sql]
(try
(.prepareStatement con sql java.sql.Statement/RETURN_GENERATED_KEYS)
(catch Exception _
;; assume it is unsupported and try basic PreparedStatement:
(.prepareStatement con sql))))))
(and result-type concurrency)
(if cursors
(fn [^Connection con ^String sql]
(.prepareStatement con sql
(get result-set-type result-type result-type)
(get result-set-concurrency concurrency concurrency)
(get result-set-holdability cursors cursors)))
(fn [^Connection con ^String sql]
(.prepareStatement con sql
(get result-set-type result-type result-type)
(get result-set-concurrency concurrency concurrency))))
(or result-type concurrency cursors)
(throw (IllegalArgumentException.
(str ":concurrency, :cursors, and :result-type "
"may not be specified independently.")))
:else
(fn [^Connection con ^String sql]
(.prepareStatement con sql)))
fetch-size (as-> f
(fn [^Connection con ^String sql]
(.setFetchSize ^PreparedStatement (f con sql) fetch-size)))
max-rows (as-> f
(fn [^Connection con ^String sql]
(.setMaxRows ^PreparedStatement (f con sql) max-rows)))
timeout (as-> f
(fn [^Connection con ^String sql]
(.setQueryTimeout ^PreparedStatement (f con sql) timeout)))))
(defn- prepare* (defn- prepare*
"Given a connection, a SQL statement, its parameters, and some options, "Given a connection, a SQL statement, its parameters, and some options,
return a PreparedStatement representing that." return a PreparedStatement representing that."
[^Connection con [sql & params] opts] [con [sql & params] opts]
(doto (.prepareStatement con sql) (set-parameters ((pre-prepare* opts) con sql) params))
(set-parameters params)))
(defn- prepare-fn*
"Given a connection, a SQL statement, its parameters, and some options,
return a PreparedStatement representing that."
[con [sql & params] factory]
(set-parameters (factory con sql) params))
(def ^:private isolation-levels (def ^:private isolation-levels
"Transaction isolation levels." "Transaction isolation levels."
@ -280,7 +370,7 @@
(defn- url+etc->datasource (defn- url+etc->datasource
"" ""
[[url etc] opts] [[url etc]]
(reify DataSource (reify DataSource
(getConnection [_] (getConnection [_]
(get-driver-connection url etc)) (get-driver-connection url etc))
@ -292,35 +382,36 @@
(extend-protocol Sourceable (extend-protocol Sourceable
clojure.lang.Associative clojure.lang.Associative
(get-datasource [this opts] (get-datasource [this]
(url+etc->datasource (spec->url+etc this) opts)) (url+etc->datasource (spec->url+etc this)))
DataSource DataSource
(get-datasource [this opts] (get-datasource [this] this)
(reify DataSource
(getConnection [_]
(.getConnection this))
(getConnection [_ username password]
(.getConnection this username password))))
String String
(get-datasource [this opts] (get-datasource [this]
(url+etc->datasource (string->url+etc this) opts))) (url+etc->datasource (string->url+etc this))))
(extend-protocol Connectable (extend-protocol Connectable
DataSource DataSource
(get-connection [this] (.getConnection this)) (get-connection [this] (.getConnection this))
Object Object
(get-connection [this] (get-connection (get-datasource this {})))) (get-connection [this] (get-connection (get-datasource this))))
(extend-protocol Preparable (extend-protocol Preparable
Connection Connection
(prepare [this sql-params opts] (prepare [this sql-params opts]
(prepare* this sql-params opts)) (prepare* this sql-params opts))
(prepare-fn [this sql-params factory]
(prepare-fn* this sql-params factory))
DataSource DataSource
(prepare [this sql-params opts] (prepare [this sql-params opts]
(prepare (.getConnection this) sql-params opts)) (prepare (.getConnection this) sql-params opts))
(prepare-fn [this sql-params factory]
(prepare-fn (.getConnection this) sql-params factory))
Object Object
(prepare [this sql-params opts] (prepare [this sql-params opts]
(prepare (get-datasource this opts) sql-params opts))) (prepare (get-datasource this) sql-params opts))
(prepare-fn [this sql-params factory]
(prepare-fn (get-datasource this) sql-params factory)))
(defn- get-column-names (defn- get-column-names
"" ""
@ -399,24 +490,26 @@
(extend-protocol Executable (extend-protocol Executable
Connection Connection
(-execute [this sql-params opts] (-execute [this sql-params opts]
(let [factory (pre-prepare* opts)]
(reify clojure.lang.IReduceInit (reify clojure.lang.IReduceInit
(reduce [_ f init] (reduce [_ f init]
(with-open [stmt (prepare this sql-params opts)] (with-open [stmt (prepare-fn this sql-params factory)]
(reduce-stmt stmt f init))))) (reduce-stmt stmt f init))))))
DataSource DataSource
(-execute [this sql-params opts] (-execute [this sql-params opts]
(let [factory (pre-prepare* opts)]
(reify clojure.lang.IReduceInit (reify clojure.lang.IReduceInit
(reduce [_ f init] (reduce [_ f init]
(with-open [con (get-connection this)] (with-open [con (get-connection this)]
(with-open [stmt (prepare con sql-params opts)] (with-open [stmt (prepare-fn con sql-params factory)]
(reduce-stmt stmt f init)))))) (reduce-stmt stmt f init)))))))
PreparedStatement PreparedStatement
(-execute [this _ _] (-execute [this _ _]
(reify clojure.lang.IReduceInit (reify clojure.lang.IReduceInit
(reduce [_ f init] (reduce-stmt this f init)))) (reduce [_ f init] (reduce-stmt this f init))))
Object Object
(-execute [this sql-params opts] (-execute [this sql-params opts]
(-execute (get-datasource this opts) sql-params opts))) (-execute (get-datasource this) sql-params opts)))
(defn query (defn query
"" ""
@ -440,9 +533,9 @@
(comment (comment
(def db-spec {:dbtype "h2:mem" :dbname "perf"}) (def db-spec {:dbtype "h2:mem" :dbname "perf"})
(def con db-spec) (def con db-spec)
(def con (get-datasource db-spec {})) (def con (get-datasource db-spec))
(get-connection con) (get-connection con)
(def con (get-connection (get-datasource db-spec {}))) (def con (get-connection (get-datasource db-spec)))
(def con (get-connection db-spec)) (def con (get-connection db-spec))
(command! con ["DROP TABLE fruit"]) (command! con ["DROP TABLE fruit"])
(command! con ["CREATE TABLE fruit (id int default 0, name varchar(32) primary key, appearance varchar(32), cost int, grade real)"]) (command! con ["CREATE TABLE fruit (id int default 0, name varchar(32) primary key, appearance varchar(32), cost int, grade real)"])