Fixes #82 and adds next.jdbc.prepare/statement
This commit is contained in:
parent
c626339681
commit
a2ba8ff780
12 changed files with 133 additions and 37 deletions
|
|
@ -6,7 +6,9 @@ Only accretive/fixative changes will be made from now on.
|
||||||
|
|
||||||
The following changes have been committed to the **master** branch since the 1.0.12 release:
|
The following changes have been committed to the **master** branch since the 1.0.12 release:
|
||||||
|
|
||||||
* None.
|
* Fix #82 by adding `clojure.java.data`-based support for setting arbitrary properties on `Connection` and `PreparedStatement` objects, post-creation. Note: this uses the Java reflection API under the hood.
|
||||||
|
* Adds `next.jdbc.prepare/statement` to create `Statement` objects with all the options available to `prepare` except `:return-keys`.
|
||||||
|
* Update `org.clojure/java.data` to 0.1.5 (for property setting).
|
||||||
|
|
||||||
## Stable Builds
|
## Stable Builds
|
||||||
|
|
||||||
|
|
|
||||||
2
deps.edn
2
deps.edn
|
|
@ -1,6 +1,6 @@
|
||||||
{:paths ["src"]
|
{:paths ["src"]
|
||||||
:deps {org.clojure/clojure {:mvn/version "1.10.1"}
|
:deps {org.clojure/clojure {:mvn/version "1.10.1"}
|
||||||
org.clojure/java.data {:mvn/version "0.1.4"}}
|
org.clojure/java.data {:mvn/version "0.1.5"}}
|
||||||
:aliases
|
:aliases
|
||||||
{:test {:extra-paths ["test"]
|
{:test {:extra-paths ["test"]
|
||||||
:extra-deps {org.clojure/test.check {:mvn/version "0.10.0"}
|
:extra-deps {org.clojure/test.check {:mvn/version "0.10.0"}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ Any path that calls `get-connection` will accept the following options:
|
||||||
|
|
||||||
* `:auto-commit` -- a `Boolean` that determines whether operations on this connection should be automatically committed (the default, `true`) or not; note that setting `:auto-commit false` is commonly required when you want to stream result set data from a query (along with fetch size etc -- see below),
|
* `:auto-commit` -- a `Boolean` that determines whether operations on this connection should be automatically committed (the default, `true`) or not; note that setting `:auto-commit false` is commonly required when you want to stream result set data from a query (along with fetch size etc -- see below),
|
||||||
* `:read-only` -- a `Boolean` that determines whether the operations on this connection should be read-only or not (the default, `false`).
|
* `:read-only` -- a `Boolean` that determines whether the operations on this connection should be read-only or not (the default, `false`).
|
||||||
|
* `:connection` -- a hash map of camelCase properties to set on the `Connection` object after it is created; these correspond to `.set*` methods on the `Connection` class and are set via the Java reflection API (using `org.clojure/java.data`). If `:autoCommit` or `:readOnly` are provided, they will take precedence over the fast, specific options above.
|
||||||
|
|
||||||
If you need additional options set on a connection, you can either use Java interop to set them directly, or provide them as part of the "db spec" hash map passed to `get-datasource` (although then they will apply to _all_ connections obtained from that datasource).
|
If you need additional options set on a connection, you can either use Java interop to set them directly, or provide them as part of the "db spec" hash map passed to `get-datasource` (although then they will apply to _all_ connections obtained from that datasource).
|
||||||
|
|
||||||
|
|
@ -44,22 +45,26 @@ Any function that might realize a row or a result set will accept:
|
||||||
* `:label-fn` -- if `:builder-fn` is specified as one of `next.jdbc.result-set`'s `as-modified-*` builders, this option must be present and should specify a string-to-string transformation that will be applied to the column label for each returned column name.
|
* `:label-fn` -- if `:builder-fn` is specified as one of `next.jdbc.result-set`'s `as-modified-*` builders, this option must be present and should specify a string-to-string transformation that will be applied to the column label for each returned column name.
|
||||||
* `:qualifier-fn` -- if `:builder-fn` is specified as one of `next.jdbc.result-set`'s `as-modified-*` builders, this option should specify a string-to-string transformation that will be applied to the table name for each returned column name for which the table name is known. It can be omitted for the `as-unqualified-modified-*` variants.
|
* `:qualifier-fn` -- if `:builder-fn` is specified as one of `next.jdbc.result-set`'s `as-modified-*` builders, this option should specify a string-to-string transformation that will be applied to the table name for each returned column name for which the table name is known. It can be omitted for the `as-unqualified-modified-*` variants.
|
||||||
|
|
||||||
## Prepared Statements
|
## Statements & Prepared Statements
|
||||||
|
|
||||||
Any function that creates a `PreparedStatement` will accept the following options:
|
Any function that creates a `Statement` or a `PreparedStatement` will accept the following options (see below for additional options for `PreparedStatement`):
|
||||||
|
|
||||||
* `:concurrency` -- a keyword that specifies the concurrency level: `:read-only`, `:updatable`,
|
* `:concurrency` -- a keyword that specifies the concurrency level: `:read-only`, `:updatable`,
|
||||||
* `:cursors` -- a keyword that specifies whether cursors should be closed or held over a commit: `:close`, `:hold`,
|
* `:cursors` -- a keyword that specifies whether cursors should be closed or held over a commit: `:close`, `:hold`,
|
||||||
* `:fetch-size` -- an integer that guides the JDBC driver in terms of how many rows to fetch at once; it is common to set `:fetch-size` to zero or a negative value in order to trigger streaming of result sets -- some JDBC drivers require additional options to be set on the connection _as well_,
|
* `:fetch-size` -- an integer that guides the JDBC driver in terms of how many rows to fetch at once; it is common to set `:fetch-size` to zero or a negative value in order to trigger streaming of result sets -- some JDBC drivers require additional options to be set on the connection _as well_,
|
||||||
* `:max-rows` -- an integer that tells the JDBC driver to limit result sets to this many rows,
|
* `:max-rows` -- an integer that tells the JDBC driver to limit result sets to this many rows,
|
||||||
* `:result-type` -- a keyword that affects how the `ResultSet` can be traversed: `:forward-only`, `:scroll-insensitive`, `:scroll-sensitive`,
|
* `:result-type` -- a keyword that affects how the `ResultSet` can be traversed: `:forward-only`, `:scroll-insensitive`, `:scroll-sensitive`,
|
||||||
* `:return-keys` -- a truthy value asks that the JDBC driver to return any generated keys created by the operation; it can be `true` or it can be a vector of keywords identifying column names that should be returned,
|
* `:timeout` -- an integer that specifies the (query) timeout allowed for SQL operations.
|
||||||
* `:timeout` -- an integer that specifies the timeout allowed for SQL operations.
|
* `:statement` -- a hash map of camelCase properties to set on the `Statement` or `PreparedStatement` object after it is created; these correspond to `.set*` methods on the `Statement` class (which `PreparedStatement` inherits) and are set via the Java reflection API (using `org.clojure/java.data`). If `:fetchSize`, `:maxRows`, or `:queryTimeout` are provided, they will take precedence over the fast, specific options above.
|
||||||
|
|
||||||
If you specify either `:concurrency` or `:result-type`, you must specify _both_ of them. If you specify `:cursors`, you must also specify `:result-type` _and_ `:concurrency`.
|
If you specify either `:concurrency` or `:result-type`, you must specify _both_ of them. If you specify `:cursors`, you must also specify `:result-type` _and_ `:concurrency`.
|
||||||
|
|
||||||
> Note: For MS SQL Server to return table names (for qualified column names), you must specify `:result-type` with one of the scroll values (and so you must also specify `:concurrency`).
|
> Note: For MS SQL Server to return table names (for qualified column names), you must specify `:result-type` with one of the scroll values (and so you must also specify `:concurrency`).
|
||||||
|
|
||||||
|
Any function that creates a `PreparedStatement` will additionally accept the following options:
|
||||||
|
|
||||||
|
* `:return-keys` -- a truthy value asks that the JDBC driver to return any generated keys created by the operation; it can be `true` or it can be a vector of keywords identifying column names that should be returned.
|
||||||
|
|
||||||
Not all databases or drivers support all of these options, or all values for any given option. If `:return-keys` is a vector of column names and that is not supported, `next.jdbc` will attempt a generic "return generated keys" option instead. If that is not supported, `next.jdbc` will fall back to a regular SQL operation. If other options are not supported, you may get a `SQLException`.
|
Not all databases or drivers support all of these options, or all values for any given option. If `:return-keys` is a vector of column names and that is not supported, `next.jdbc` will attempt a generic "return generated keys" option instead. If that is not supported, `next.jdbc` will fall back to a regular SQL operation. If other options are not supported, you may get a `SQLException`.
|
||||||
|
|
||||||
In addition, `next.jdbc.prepare/execute-batch!` accepts an options hash map that can contain the following:
|
In addition, `next.jdbc.prepare/execute-batch!` accepts an options hash map that can contain the following:
|
||||||
|
|
|
||||||
|
|
@ -185,13 +185,15 @@ You can read more about [working with transactions](/doc/transactions.md) furthe
|
||||||
|
|
||||||
### Prepared Statement Caveat
|
### Prepared Statement Caveat
|
||||||
|
|
||||||
Not all databases support using a `PreparedStatement` for every type of SQL operation. You might have to create a `java.sql.Statement` yourself, directly from a `java.sql.Connection` and use that, without parameters, in `plan`, `execute!`, or `execute-one!`. See the following example:
|
Not all databases support using a `PreparedStatement` for every type of SQL operation. You might have to create a `java.sql.Statement` instead, directly from a `java.sql.Connection` and use that, without parameters, in `plan`, `execute!`, or `execute-one!`. See the following example:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
|
(require '[next.jdbc.prepare :as prep])
|
||||||
|
|
||||||
(with-open [con (jdbc/get-connection ds)]
|
(with-open [con (jdbc/get-connection ds)]
|
||||||
(jdbc/execute! (.createStatement con) ["...just a SQL string..."])
|
(jdbc/execute! (prep/statement con) ["...just a SQL string..."])
|
||||||
(jdbc/execute! con ["...some SQL..." "and" "parameters"]) ; uses PreparedStatement
|
(jdbc/execute! con ["...some SQL..." "and" "parameters"]) ; uses PreparedStatement
|
||||||
(into [] (map :column) (jdbc/plan (.createStatement con) ["..."])))
|
(into [] (map :column) (jdbc/plan (prep/statement con) ["..."])))
|
||||||
```
|
```
|
||||||
|
|
||||||
## Connection Pooling
|
## Connection Pooling
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,18 @@
|
||||||
|
|
||||||
Under the hood, whenever you ask `next.jdbc` to execute some SQL it creates a `java.sql.PreparedStatement`, adds in the parameters you provide, and then calls `.execute` on it. Then it attempts to get a `ResultSet` from that and either return it or process it. If you asked for generated keys to be returned, that `ResultSet` will contain those generated keys if your database supports it, otherwise it will be whatever the `.execute` function produces. If no `ResultSet` is available at all, `next.jdbc` will ask for the count of updated rows and return that as if it were a result set.
|
Under the hood, whenever you ask `next.jdbc` to execute some SQL it creates a `java.sql.PreparedStatement`, adds in the parameters you provide, and then calls `.execute` on it. Then it attempts to get a `ResultSet` from that and either return it or process it. If you asked for generated keys to be returned, that `ResultSet` will contain those generated keys if your database supports it, otherwise it will be whatever the `.execute` function produces. If no `ResultSet` is available at all, `next.jdbc` will ask for the count of updated rows and return that as if it were a result set.
|
||||||
|
|
||||||
> Note: Some databases do not support all SQL operations via `PreparedStatement`, in which case you may need to create a `java.sql.Statement` and pass that into `plan`, `execute!`, or `execute-one!`, along with the SQL you wish to execute. Note that such statement execution may not have parameters. See the [Prepared Statement Caveat in Getting Started](/doc/getting-started.md#prepared-statement-caveat) for an example.
|
> Note: Some databases do not support all SQL operations via `PreparedStatement`, in which case you may need to create a `java.sql.Statement` instead and pass that into `plan`, `execute!`, or `execute-one!`, along with the SQL you wish to execute. Note that such statement execution may not have parameters. See the [Prepared Statement Caveat in Getting Started](/doc/getting-started.md#prepared-statement-caveat) for an example.
|
||||||
|
|
||||||
If you have a SQL operation that you intend to run multiple times on the same `java.sql.Connection`, it may be worth creating the prepared statement yourself and reusing it. `next.jdbc/prepare` accepts a connection and a vector of SQL and optional parameters and returns a `java.sql.PreparedStatement` which can be passed to `plan`, `execute!`, or `execute-one!` as the first argument. It is your responsibility to close the prepared statement after it has been used.
|
If you have a SQL operation that you intend to run multiple times on the same `java.sql.Connection`, it may be worth creating the prepared statement yourself and reusing it. `next.jdbc/prepare` accepts a connection and a vector of SQL and optional parameters and returns a `java.sql.PreparedStatement` which can be passed to `plan`, `execute!`, or `execute-one!` as the first argument. It is your responsibility to close the prepared statement after it has been used.
|
||||||
|
|
||||||
If you need to pass an option map to `plan`, `execute!`, or `execute-one!` when passing a prepared statement, you must pass `nil` or `[]` as the second argument:
|
If you need to pass an option map to `plan`, `execute!`, or `execute-one!` when passing a statement or prepared statement, you must pass `nil` or `[]` as the second argument:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(with-open [con (jdbc/get-connection ds)]
|
(with-open [con (jdbc/get-connection ds)]
|
||||||
(with-open [ps (jdbc/prepare con ["..." ...])]
|
(with-open [ps (jdbc/prepare con ["..." ...])]
|
||||||
(jdbc/execute-one! ps nil {...})))
|
(jdbc/execute-one! ps nil {...})))
|
||||||
|
(with-open [stmt (jdbc/statement con)]
|
||||||
|
(jdbc/execute-one! stmt nil {...})))
|
||||||
```
|
```
|
||||||
|
|
||||||
You can provide the parameters in the `prepare` call or you can provide them via a call to `set-parameters` (discussed in more detail below).
|
You can provide the parameters in the `prepare` call or you can provide them via a call to `set-parameters` (discussed in more detail below).
|
||||||
|
|
|
||||||
|
|
@ -38,14 +38,25 @@
|
||||||
column names followed by vectors of column values for each row, and
|
column names followed by vectors of column values for each row, and
|
||||||
lower-case variants of each.
|
lower-case variants of each.
|
||||||
|
|
||||||
The following options are supported wherever a `PreparedStatement` is created:
|
The following options are supported wherever a `Connection` is created:
|
||||||
|
* `:auto-commit` -- either `true` or `false`,
|
||||||
|
* `:read-only` -- either `true` or `false`,
|
||||||
|
* `:connection` -- a hash map of camelCase properties to set, via reflection,
|
||||||
|
on the `Connection` object after it is created.
|
||||||
|
|
||||||
|
The following options are supported wherever a `Statement` or
|
||||||
|
`PreparedStatement` is created:
|
||||||
* `:concurrency` -- `:read-only`, `:updatable`,
|
* `:concurrency` -- `:read-only`, `:updatable`,
|
||||||
* `:cursors` -- `:close`, `:hold`
|
* `:cursors` -- `:close`, `:hold`
|
||||||
* `:fetch-size` -- the fetch size value,
|
* `:fetch-size` -- the fetch size value,
|
||||||
* `:max-rows` -- the maximum number of rows to return,
|
* `:max-rows` -- the maximum number of rows to return,
|
||||||
* `:result-type` -- `:forward-only`, `:scroll-insensitive`, `:scroll-sensitive`,
|
* `:result-type` -- `:forward-only`, `:scroll-insensitive`, `:scroll-sensitive`,
|
||||||
* `:return-keys` -- either `true` or a vector of key names to return,
|
* `:timeout` -- the query timeout,
|
||||||
* `:timeout` -- the query timeout."
|
* `:statement` -- a hash map of camelCase properties to set, via reflection,
|
||||||
|
on the `Statement` or `PreparedStatement` object after it is created.
|
||||||
|
|
||||||
|
In addition, wherever a `PreparedStatement` is created, you may specify:
|
||||||
|
* `:return-keys` -- either `true` or a vector of key names to return."
|
||||||
(:require [next.jdbc.connection]
|
(:require [next.jdbc.connection]
|
||||||
[next.jdbc.prepare]
|
[next.jdbc.prepare]
|
||||||
[next.jdbc.protocols :as p]
|
[next.jdbc.protocols :as p]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
Also provides `dbtypes` as a map of all known database types, and
|
Also provides `dbtypes` as a map of all known database types, and
|
||||||
the `->pool` function for creating pooled datasource objects."
|
the `->pool` function for creating pooled datasource objects."
|
||||||
(:require [clojure.java.data :refer [to-java]]
|
(:require [clojure.java.data :as j]
|
||||||
[next.jdbc.protocols :as p])
|
[next.jdbc.protocols :as p])
|
||||||
(:import (java.sql Connection DriverManager)
|
(:import (java.sql Connection DriverManager)
|
||||||
(javax.sql DataSource)
|
(javax.sql DataSource)
|
||||||
|
|
@ -221,9 +221,9 @@
|
||||||
`java.io.Closeable` (HikariCP does, c3p0 does not)."
|
`java.io.Closeable` (HikariCP does, c3p0 does not)."
|
||||||
[clazz db-spec]
|
[clazz db-spec]
|
||||||
(if (:jdbcUrl db-spec)
|
(if (:jdbcUrl db-spec)
|
||||||
(to-java clazz db-spec)
|
(j/to-java clazz db-spec)
|
||||||
(let [[url etc] (spec->url+etc db-spec)]
|
(let [[url etc] (spec->url+etc db-spec)]
|
||||||
(to-java clazz (assoc etc :jdbcUrl url)))))
|
(j/to-java clazz (assoc etc :jdbcUrl url)))))
|
||||||
|
|
||||||
(defn- string->url+etc
|
(defn- string->url+etc
|
||||||
"Given a JDBC URL, return it with an empty set of options with no parsing."
|
"Given a JDBC URL, return it with an empty set of options with no parsing."
|
||||||
|
|
@ -248,17 +248,23 @@
|
||||||
"Given a `DataSource` and a map of options, get a connection and update it
|
"Given a `DataSource` and a map of options, get a connection and update it
|
||||||
as specified by the options.
|
as specified by the options.
|
||||||
|
|
||||||
The options supported are:
|
These options are supported:
|
||||||
* `:auto-commit` -- whether the connection should be set to auto-commit or not;
|
* `:auto-commit` -- whether the connection should be set to auto-commit or not;
|
||||||
without this option, the defaut is `true` -- connections will auto-commit,
|
without this option, the defaut is `true` -- connections will auto-commit,
|
||||||
* `:read-only` -- whether the connection should be set to read-only mode."
|
* `:read-only` -- whether the connection should be set to read-only mode,
|
||||||
|
* `:connection` -- a hash map of camelCase properties to set on the connection,
|
||||||
|
via reflection, e.g., :autoCommit, :readOnly, :schema..."
|
||||||
^Connection
|
^Connection
|
||||||
[^DataSource datasource opts]
|
[^DataSource datasource opts]
|
||||||
(let [^Connection connection (.getConnection datasource)]
|
(let [^Connection connection (.getConnection datasource)]
|
||||||
|
;; fast, specific option handling:
|
||||||
(when (contains? opts :auto-commit)
|
(when (contains? opts :auto-commit)
|
||||||
(.setAutoCommit connection (boolean (:auto-commit opts))))
|
(.setAutoCommit connection (boolean (:auto-commit opts))))
|
||||||
(when (contains? opts :read-only)
|
(when (contains? opts :read-only)
|
||||||
(.setReadOnly connection (boolean (:read-only opts))))
|
(.setReadOnly connection (boolean (:read-only opts))))
|
||||||
|
;; slow, general-purpose option handling:
|
||||||
|
(when-let [props (:connection opts)]
|
||||||
|
(j/set-properties connection props))
|
||||||
connection))
|
connection))
|
||||||
|
|
||||||
(extend-protocol p/Sourceable
|
(extend-protocol p/Sourceable
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@
|
||||||
|
|
||||||
Defines the `SettableParameter` protocol for converting Clojure values
|
Defines the `SettableParameter` protocol for converting Clojure values
|
||||||
to database-specific values."
|
to database-specific values."
|
||||||
(:require [next.jdbc.protocols :as p])
|
(:require [clojure.java.data :as j]
|
||||||
|
[next.jdbc.protocols :as p])
|
||||||
(:import (java.sql Connection
|
(:import (java.sql Connection
|
||||||
PreparedStatement
|
PreparedStatement
|
||||||
ResultSet
|
ResultSet
|
||||||
|
|
@ -82,7 +83,8 @@
|
||||||
^java.sql.PreparedStatement
|
^java.sql.PreparedStatement
|
||||||
[^Connection con ^String sql params
|
[^Connection con ^String sql params
|
||||||
{:keys [return-keys result-type concurrency cursors
|
{:keys [return-keys result-type concurrency cursors
|
||||||
fetch-size max-rows timeout]}]
|
fetch-size max-rows timeout]
|
||||||
|
:as opts}]
|
||||||
(let [^PreparedStatement ps
|
(let [^PreparedStatement ps
|
||||||
(cond
|
(cond
|
||||||
return-keys
|
return-keys
|
||||||
|
|
@ -124,12 +126,16 @@
|
||||||
"may not be specified independently.")))
|
"may not be specified independently.")))
|
||||||
:else
|
:else
|
||||||
(.prepareStatement con sql))]
|
(.prepareStatement con sql))]
|
||||||
|
;; fast, specific option handling:
|
||||||
(when fetch-size
|
(when fetch-size
|
||||||
(.setFetchSize ps fetch-size))
|
(.setFetchSize ps fetch-size))
|
||||||
(when max-rows
|
(when max-rows
|
||||||
(.setMaxRows ps max-rows))
|
(.setMaxRows ps max-rows))
|
||||||
(when timeout
|
(when timeout
|
||||||
(.setQueryTimeout ps timeout))
|
(.setQueryTimeout ps timeout))
|
||||||
|
;; slow, general-purpose option handling:
|
||||||
|
(when-let [props (:statement opts)]
|
||||||
|
(j/set-properties ps props))
|
||||||
(set-parameters ps params)))
|
(set-parameters ps params)))
|
||||||
|
|
||||||
(extend-protocol p/Preparable
|
(extend-protocol p/Preparable
|
||||||
|
|
@ -137,6 +143,44 @@
|
||||||
(prepare [this sql-params opts]
|
(prepare [this sql-params opts]
|
||||||
(create this (first sql-params) (rest sql-params) opts)))
|
(create this (first sql-params) (rest sql-params) opts)))
|
||||||
|
|
||||||
|
(defn statement
|
||||||
|
"Given a `Connection` and some options, return a `Statement`."
|
||||||
|
^java.sql.Statement
|
||||||
|
([con] (statement con {}))
|
||||||
|
([^Connection con
|
||||||
|
{:keys [result-type concurrency cursors
|
||||||
|
fetch-size max-rows timeout]
|
||||||
|
:as opts}]
|
||||||
|
(let [^Statement stmt
|
||||||
|
(cond
|
||||||
|
(and result-type concurrency)
|
||||||
|
(if cursors
|
||||||
|
(.createStatement con
|
||||||
|
(get result-set-type result-type result-type)
|
||||||
|
(get result-set-concurrency concurrency concurrency)
|
||||||
|
(get result-set-holdability cursors cursors))
|
||||||
|
(.createStatement con
|
||||||
|
(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
|
||||||
|
(.createStatement con))]
|
||||||
|
;; fast, specific option handling:
|
||||||
|
(when fetch-size
|
||||||
|
(.setFetchSize stmt fetch-size))
|
||||||
|
(when max-rows
|
||||||
|
(.setMaxRows stmt max-rows))
|
||||||
|
(when timeout
|
||||||
|
(.setQueryTimeout stmt timeout))
|
||||||
|
;; slow, general-purpose option handling:
|
||||||
|
(when-let [props (:statement opts)]
|
||||||
|
(j/set-properties stmt props))
|
||||||
|
stmt)))
|
||||||
|
|
||||||
(defn execute-batch!
|
(defn execute-batch!
|
||||||
"Given a `PreparedStatement` and a vector containing parameter groups,
|
"Given a `PreparedStatement` and a vector containing parameter groups,
|
||||||
i.e., a vector of vector of parameters, use `.addBatch` to add each group
|
i.e., a vector of vector of parameters, use `.addBatch` to add each group
|
||||||
|
|
|
||||||
|
|
@ -652,7 +652,7 @@
|
||||||
[{:next.jdbc/update-count (.getUpdateCount this)}]))
|
[{:next.jdbc/update-count (.getUpdateCount this)}]))
|
||||||
|
|
||||||
java.sql.Statement
|
java.sql.Statement
|
||||||
;; we can't tell if this PreparedStatement will return generated
|
;; we can't tell if this Statement will return generated
|
||||||
;; keys so we pass a truthy value to at least attempt it if we
|
;; keys so we pass a truthy value to at least attempt it if we
|
||||||
;; do not get a ResultSet back from the execute call
|
;; do not get a ResultSet back from the execute call
|
||||||
(-execute [this sql-params opts]
|
(-execute [this sql-params opts]
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
[next.jdbc.connection :as connection]
|
[next.jdbc.connection :as connection]
|
||||||
[next.jdbc.prepare :as prepare]
|
[next.jdbc.prepare :as prepare]
|
||||||
[next.jdbc.sql :as sql])
|
[next.jdbc.sql :as sql])
|
||||||
(:import (java.sql Connection PreparedStatement)
|
(:import (java.sql Connection PreparedStatement Statement)
|
||||||
(javax.sql DataSource)))
|
(javax.sql DataSource)))
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
(s/def ::connection #(instance? Connection %))
|
(s/def ::connection #(instance? Connection %))
|
||||||
(s/def ::datasource #(instance? DataSource %))
|
(s/def ::datasource #(instance? DataSource %))
|
||||||
(s/def ::prepared-statement #(instance? PreparedStatement %))
|
(s/def ::prepared-statement #(instance? PreparedStatement %))
|
||||||
|
(s/def ::statement #(instance? Statement %))
|
||||||
|
|
||||||
(s/def ::db-spec (s/or :db-spec ::db-spec-map
|
(s/def ::db-spec (s/or :db-spec ::db-spec-map
|
||||||
:jdbc-url ::jdbc-url-map
|
:jdbc-url ::jdbc-url-map
|
||||||
|
|
@ -90,20 +91,24 @@
|
||||||
:sql-params ::sql-params
|
:sql-params ::sql-params
|
||||||
:opts (s/? ::opts-map)))
|
:opts (s/? ::opts-map)))
|
||||||
|
|
||||||
|
(s/fdef jdbc/statement
|
||||||
|
:args (s/cat :connection ::connection
|
||||||
|
:opts (s/? ::opts-map)))
|
||||||
|
|
||||||
(s/fdef jdbc/plan
|
(s/fdef jdbc/plan
|
||||||
:args (s/alt :prepared (s/cat :stmt ::prepared-statement)
|
:args (s/alt :prepared (s/cat :stmt ::statement)
|
||||||
:sql (s/cat :connectable ::connectable
|
:sql (s/cat :connectable ::connectable
|
||||||
:sql-params (s/nilable ::sql-params)
|
:sql-params (s/nilable ::sql-params)
|
||||||
:opts (s/? ::opts-map))))
|
:opts (s/? ::opts-map))))
|
||||||
|
|
||||||
(s/fdef jdbc/execute!
|
(s/fdef jdbc/execute!
|
||||||
:args (s/alt :prepared (s/cat :stmt ::prepared-statement)
|
:args (s/alt :prepared (s/cat :stmt ::statement)
|
||||||
:sql (s/cat :connectable ::connectable
|
:sql (s/cat :connectable ::connectable
|
||||||
:sql-params (s/nilable ::sql-params)
|
:sql-params (s/nilable ::sql-params)
|
||||||
:opts (s/? ::opts-map))))
|
:opts (s/? ::opts-map))))
|
||||||
|
|
||||||
(s/fdef jdbc/execute-one!
|
(s/fdef jdbc/execute-one!
|
||||||
:args (s/alt :prepared (s/cat :stmt ::prepared-statement)
|
:args (s/alt :prepared (s/cat :stmt ::statement)
|
||||||
:sql (s/cat :connectable ::connectable
|
:sql (s/cat :connectable ::connectable
|
||||||
:sql-params (s/nilable ::sql-params)
|
:sql-params (s/nilable ::sql-params)
|
||||||
:opts (s/? ::opts-map))))
|
:opts (s/? ::opts-map))))
|
||||||
|
|
@ -201,6 +206,7 @@
|
||||||
`connection/->pool
|
`connection/->pool
|
||||||
`prepare/execute-batch!
|
`prepare/execute-batch!
|
||||||
`prepare/set-parameters
|
`prepare/set-parameters
|
||||||
|
`prepare/statement
|
||||||
`sql/insert!
|
`sql/insert!
|
||||||
`sql/insert-multi!
|
`sql/insert-multi!
|
||||||
`sql/query
|
`sql/query
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
"Multi-database testing fixtures."
|
"Multi-database testing fixtures."
|
||||||
(:require [clojure.string :as str]
|
(:require [clojure.string :as str]
|
||||||
[next.jdbc :as jdbc]
|
[next.jdbc :as jdbc]
|
||||||
|
[next.jdbc.prepare :as prep]
|
||||||
[next.jdbc.sql :as sql])
|
[next.jdbc.sql :as sql])
|
||||||
(:import (com.opentable.db.postgres.embedded EmbeddedPostgres)
|
(:import (com.opentable.db.postgres.embedded EmbeddedPostgres)
|
||||||
(javax.sql DataSource)))
|
(javax.sql DataSource)))
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,8 @@
|
||||||
(is (= (column :FRUIT/ID) (ffirst rs)))
|
(is (= (column :FRUIT/ID) (ffirst rs)))
|
||||||
;; and all its corresponding values should be ints
|
;; and all its corresponding values should be ints
|
||||||
(is (every? int? (map first (rest rs))))
|
(is (every? int? (map first (rest rs))))
|
||||||
(is (every? string? (map second (rest rs)))))
|
(is (every? string? (map second (rest rs))))))
|
||||||
|
(testing "execute! with adapter"
|
||||||
(let [rs (jdbc/execute! ; test again, with adapter and lower columns
|
(let [rs (jdbc/execute! ; test again, with adapter and lower columns
|
||||||
(ds)
|
(ds)
|
||||||
["select * from fruit order by id"]
|
["select * from fruit order by id"]
|
||||||
|
|
@ -89,7 +90,8 @@
|
||||||
(is (= :fruit/id (ffirst rs)))
|
(is (= :fruit/id (ffirst rs)))
|
||||||
;; and all its corresponding values should be ints
|
;; and all its corresponding values should be ints
|
||||||
(is (every? int? (map first (rest rs))))
|
(is (every? int? (map first (rest rs))))
|
||||||
(is (every? string? (map second (rest rs)))))
|
(is (every? string? (map second (rest rs))))))
|
||||||
|
(testing "execute! with unqualified"
|
||||||
(let [rs (jdbc/execute!
|
(let [rs (jdbc/execute!
|
||||||
(ds)
|
(ds)
|
||||||
["select * from fruit order by id"]
|
["select * from fruit order by id"]
|
||||||
|
|
@ -113,6 +115,25 @@
|
||||||
;; and all its corresponding values should be ints
|
;; and all its corresponding values should be ints
|
||||||
(is (every? int? (map first (rest rs))))
|
(is (every? int? (map first (rest rs))))
|
||||||
(is (every? string? (map second (rest rs))))))
|
(is (every? string? (map second (rest rs))))))
|
||||||
|
(testing "execute! with :max-rows / :maxRows"
|
||||||
|
(let [rs (jdbc/execute!
|
||||||
|
(ds)
|
||||||
|
["select * from fruit order by id"]
|
||||||
|
(assoc (default-options) :max-rows 2))]
|
||||||
|
(is (every? map? rs))
|
||||||
|
(is (every? meta rs))
|
||||||
|
(is (= 2 (count rs)))
|
||||||
|
(is (= 1 ((column :FRUIT/ID) (first rs))))
|
||||||
|
(is (= 2 ((column :FRUIT/ID) (last rs)))))
|
||||||
|
(let [rs (jdbc/execute!
|
||||||
|
(ds)
|
||||||
|
["select * from fruit order by id"]
|
||||||
|
(assoc (default-options) :statement {:maxRows 2}))]
|
||||||
|
(is (every? map? rs))
|
||||||
|
(is (every? meta rs))
|
||||||
|
(is (= 2 (count rs)))
|
||||||
|
(is (= 1 ((column :FRUIT/ID) (first rs))))
|
||||||
|
(is (= 2 ((column :FRUIT/ID) (last rs))))))
|
||||||
(testing "prepare"
|
(testing "prepare"
|
||||||
(let [rs (with-open [con (jdbc/get-connection (ds))
|
(let [rs (with-open [con (jdbc/get-connection (ds))
|
||||||
ps (jdbc/prepare
|
ps (jdbc/prepare
|
||||||
|
|
@ -137,24 +158,20 @@
|
||||||
(is (= 4 ((column :FRUIT/ID) (first rs))))))
|
(is (= 4 ((column :FRUIT/ID) (first rs))))))
|
||||||
(testing "statement"
|
(testing "statement"
|
||||||
(let [rs (with-open [con (jdbc/get-connection (ds))]
|
(let [rs (with-open [con (jdbc/get-connection (ds))]
|
||||||
(jdbc/execute! (.createStatement con)
|
(jdbc/execute! (prep/statement con (default-options))
|
||||||
["select * from fruit order by id"]))]
|
["select * from fruit order by id"]))]
|
||||||
(is (every? map? rs))
|
(is (every? map? rs))
|
||||||
(is (every? meta rs))
|
(is (every? meta rs))
|
||||||
(is (= 4 (count rs)))
|
(is (= 4 (count rs)))
|
||||||
;; SQL Server only returns table name if result-type/concurrency
|
(is (= 1 ((column :FRUIT/ID) (first rs))))
|
||||||
;; provided, which we can only for a PreparedStatement
|
(is (= 4 ((column :FRUIT/ID) (last rs)))))
|
||||||
(is (= 1 ((if (mssql?) :ID (column :FRUIT/ID)) (first rs))))
|
|
||||||
(is (= 4 ((if (mssql?) :ID (column :FRUIT/ID)) (last rs)))))
|
|
||||||
(let [rs (with-open [con (jdbc/get-connection (ds))]
|
(let [rs (with-open [con (jdbc/get-connection (ds))]
|
||||||
(jdbc/execute! (.createStatement con)
|
(jdbc/execute! (prep/statement con (default-options))
|
||||||
["select * from fruit where id = 4"]))]
|
["select * from fruit where id = 4"]))]
|
||||||
(is (every? map? rs))
|
(is (every? map? rs))
|
||||||
(is (every? meta rs))
|
(is (every? meta rs))
|
||||||
(is (= 1 (count rs)))
|
(is (= 1 (count rs)))
|
||||||
;; SQL Server only returns table name if result-type/concurrency
|
(is (= 4 ((column :FRUIT/ID) (first rs))))))
|
||||||
;; provided, which we can only for a PreparedStatement
|
|
||||||
(is (= 4 ((if (mssql?) :ID (column :FRUIT/ID)) (first rs))))))
|
|
||||||
(testing "transact"
|
(testing "transact"
|
||||||
(is (= [{:next.jdbc/update-count 1}]
|
(is (= [{:next.jdbc/update-count 1}]
|
||||||
(jdbc/transact (ds)
|
(jdbc/transact (ds)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue