2019-03-31 23:54:34 +00:00
|
|
|
;; copyright (c) 2018-2019 Sean Corfield, all rights reserved
|
|
|
|
|
|
|
|
|
|
(ns next.jdbc.prepare
|
2019-04-21 23:13:52 +00:00
|
|
|
"Mostly an implementation namespace for how `PreparedStatement objects` are
|
2019-04-01 06:17:12 +00:00
|
|
|
created by the next generation java.jdbc library.
|
|
|
|
|
|
2019-04-21 23:13:52 +00:00
|
|
|
`set-parameters` is public and may be useful if you have a `PreparedStatement`
|
2019-04-18 06:43:32 +00:00
|
|
|
that you wish to reuse and (re)set the parameters on it.
|
|
|
|
|
|
2019-04-21 23:13:52 +00:00
|
|
|
Defines the `SettableParameter` protocol for converting Clojure values
|
2019-04-18 06:43:32 +00:00
|
|
|
to database-specific values."
|
2019-03-31 23:54:34 +00:00
|
|
|
(:require [next.jdbc.protocols :as p])
|
|
|
|
|
(:import (java.sql Connection
|
|
|
|
|
PreparedStatement
|
|
|
|
|
ResultSet
|
|
|
|
|
Statement)))
|
|
|
|
|
|
|
|
|
|
(set! *warn-on-reflection* true)
|
|
|
|
|
|
2019-04-02 06:32:24 +00:00
|
|
|
(defprotocol SettableParameter :extend-via-metadata true
|
2019-04-02 06:25:10 +00:00
|
|
|
"Protocol for setting SQL parameters in statement objects, which
|
|
|
|
|
can convert from Clojure values. The default implementation just
|
2019-04-21 23:13:52 +00:00
|
|
|
calls `.setObject` on the parameter value. It can be extended to use other
|
|
|
|
|
methods of `PreparedStatement` to convert and set parameter values."
|
2019-04-02 06:25:10 +00:00
|
|
|
(set-parameter [val stmt ix]
|
|
|
|
|
"Convert a Clojure value into a SQL value and store it as the ix'th
|
|
|
|
|
parameter in the given SQL statement object."))
|
|
|
|
|
|
2019-04-02 06:32:24 +00:00
|
|
|
(extend-protocol SettableParameter
|
2019-04-02 06:25:10 +00:00
|
|
|
Object
|
|
|
|
|
(set-parameter [v ^PreparedStatement s ^long i]
|
|
|
|
|
(.setObject s i v))
|
|
|
|
|
|
|
|
|
|
nil
|
|
|
|
|
(set-parameter [_ ^PreparedStatement s ^long i]
|
|
|
|
|
(.setObject s i nil)))
|
|
|
|
|
|
2019-03-31 23:54:34 +00:00
|
|
|
(defn set-parameters
|
2019-04-21 23:13:52 +00:00
|
|
|
"Given a `PreparedStatement` and a vector of parameter values, update the
|
|
|
|
|
`PreparedStatement` with those parameters and return it."
|
2019-03-31 23:54:34 +00:00
|
|
|
^java.sql.PreparedStatement
|
|
|
|
|
[^PreparedStatement ps params]
|
|
|
|
|
(when (seq params)
|
|
|
|
|
(loop [[p & more] params i 1]
|
2019-04-02 06:25:10 +00:00
|
|
|
(set-parameter p ps i)
|
2019-03-31 23:54:34 +00:00
|
|
|
(when more
|
|
|
|
|
(recur more (inc i)))))
|
|
|
|
|
ps)
|
|
|
|
|
|
|
|
|
|
(def ^{:private true
|
2019-04-21 23:13:52 +00:00
|
|
|
:doc "Map friendly `:concurrency` values to `ResultSet` constants."}
|
2019-03-31 23:54:34 +00:00
|
|
|
result-set-concurrency
|
|
|
|
|
{:read-only ResultSet/CONCUR_READ_ONLY
|
|
|
|
|
:updatable ResultSet/CONCUR_UPDATABLE})
|
|
|
|
|
|
|
|
|
|
(def ^{:private true
|
2019-04-21 23:13:52 +00:00
|
|
|
:doc "Map friendly `:cursors` values to `ResultSet` constants."}
|
2019-03-31 23:54:34 +00:00
|
|
|
result-set-holdability
|
|
|
|
|
{:hold ResultSet/HOLD_CURSORS_OVER_COMMIT
|
|
|
|
|
:close ResultSet/CLOSE_CURSORS_AT_COMMIT})
|
|
|
|
|
|
|
|
|
|
(def ^{:private true
|
2019-04-21 23:13:52 +00:00
|
|
|
:doc "Map friendly `:type` values to `ResultSet` constants."}
|
2019-03-31 23:54:34 +00:00
|
|
|
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))
|
|
|
|
|
|
2019-04-11 04:46:38 +00:00
|
|
|
(defn create
|
2019-04-21 23:13:52 +00:00
|
|
|
"Given a `Connection`, a SQL string, some parameters, and some options,
|
|
|
|
|
return a `PreparedStatement` representing that."
|
2019-04-11 04:46:38 +00:00
|
|
|
^java.sql.PreparedStatement
|
|
|
|
|
[^Connection con ^String sql params
|
|
|
|
|
{:keys [return-keys result-type concurrency cursors
|
2019-03-31 23:54:34 +00:00
|
|
|
fetch-size max-rows timeout]}]
|
2019-04-11 04:46:38 +00:00
|
|
|
(let [^PreparedStatement ps
|
|
|
|
|
(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)]
|
2019-03-31 23:54:34 +00:00
|
|
|
(try
|
2019-04-11 04:46:38 +00:00
|
|
|
(try
|
|
|
|
|
(.prepareStatement con sql key-names)
|
|
|
|
|
(catch Exception _
|
|
|
|
|
;; assume it is unsupported and try regular generated keys:
|
|
|
|
|
(.prepareStatement con sql Statement/RETURN_GENERATED_KEYS)))
|
2019-03-31 23:54:34 +00:00
|
|
|
(catch Exception _
|
2019-04-11 04:46:38 +00:00
|
|
|
;; assume it is unsupported and try basic PreparedStatement:
|
|
|
|
|
(.prepareStatement con sql))))
|
|
|
|
|
(try
|
|
|
|
|
(.prepareStatement con sql Statement/RETURN_GENERATED_KEYS)
|
2019-03-31 23:54:34 +00:00
|
|
|
(catch Exception _
|
|
|
|
|
;; assume it is unsupported and try basic PreparedStatement:
|
|
|
|
|
(.prepareStatement con sql)))))
|
|
|
|
|
|
2019-04-11 04:46:38 +00:00
|
|
|
(and result-type concurrency)
|
|
|
|
|
(if cursors
|
|
|
|
|
(.prepareStatement con sql
|
|
|
|
|
(get result-set-type result-type result-type)
|
|
|
|
|
(get result-set-concurrency concurrency concurrency)
|
|
|
|
|
(get result-set-holdability cursors cursors))
|
|
|
|
|
(.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
|
|
|
|
|
(.prepareStatement con sql))]
|
|
|
|
|
(when fetch-size
|
|
|
|
|
(.setFetchSize ps fetch-size))
|
|
|
|
|
(when max-rows
|
|
|
|
|
(.setMaxRows ps max-rows))
|
|
|
|
|
(when timeout
|
|
|
|
|
(.setQueryTimeout ps timeout))
|
|
|
|
|
(set-parameters ps params)))
|
2019-03-31 23:54:34 +00:00
|
|
|
|
|
|
|
|
(extend-protocol p/Preparable
|
|
|
|
|
java.sql.Connection
|
|
|
|
|
(prepare [this sql-params opts]
|
2019-04-11 04:46:38 +00:00
|
|
|
(create this (first sql-params) (rest sql-params) opts)))
|