2021-01-30 00:13:05 +00:00
|
|
|
;; copyright (c) 2019-2021 Sean Corfield, all rights reserved
|
2019-05-22 04:45:05 +00:00
|
|
|
|
|
|
|
|
(ns next.jdbc.specs
|
2019-05-22 04:55:10 +00:00
|
|
|
"Specs for the core API of next.jdbc.
|
|
|
|
|
|
2019-07-10 19:11:26 +00:00
|
|
|
The functions from `next.jdbc`, `next.jdbc.sql`, and `next.jdbc.prepare`
|
|
|
|
|
have specs here.
|
|
|
|
|
|
2019-05-22 04:55:10 +00:00
|
|
|
Just `:args` are spec'd. These specs are intended to aid development
|
|
|
|
|
with `next.jdbc` by catching simple errors in calling the library.
|
|
|
|
|
The `connectable` argument is currently just `any?` but both
|
|
|
|
|
`get-datasource` and `get-connection` have stricter specs. If you
|
|
|
|
|
extend `Sourceable` or `Connectable`, those specs will likely be too strict.
|
|
|
|
|
|
|
|
|
|
In addition, there is an `instrument` function that provides a simple
|
2019-09-14 20:32:34 +00:00
|
|
|
way to instrument all of the `next.jdbc` functions, and `unstrument`
|
|
|
|
|
to undo that."
|
2019-05-22 04:45:05 +00:00
|
|
|
(:require [clojure.spec.alpha :as s]
|
|
|
|
|
[clojure.spec.test.alpha :as st]
|
|
|
|
|
[next.jdbc :as jdbc]
|
2019-07-18 06:50:56 +00:00
|
|
|
[next.jdbc.connection :as connection]
|
2019-07-10 19:11:26 +00:00
|
|
|
[next.jdbc.prepare :as prepare]
|
2019-05-22 04:45:05 +00:00
|
|
|
[next.jdbc.sql :as sql])
|
2019-12-20 23:45:22 +00:00
|
|
|
(:import (java.sql Connection PreparedStatement Statement)
|
2019-05-22 04:45:05 +00:00
|
|
|
(javax.sql DataSource)))
|
|
|
|
|
|
2019-05-29 16:04:21 +00:00
|
|
|
(set! *warn-on-reflection* true)
|
|
|
|
|
|
2019-05-22 04:45:05 +00:00
|
|
|
(s/def ::dbtype string?)
|
|
|
|
|
(s/def ::dbname string?)
|
2019-07-11 22:43:49 +00:00
|
|
|
(s/def ::dbname-separator string?)
|
2019-05-22 04:45:05 +00:00
|
|
|
(s/def ::classname string?)
|
|
|
|
|
(s/def ::user string?)
|
|
|
|
|
(s/def ::password string?)
|
2019-07-11 22:43:49 +00:00
|
|
|
(s/def ::host (s/or :name string?
|
|
|
|
|
:none #{:none}))
|
|
|
|
|
(s/def ::host-prefix string?)
|
2019-05-22 04:45:05 +00:00
|
|
|
(s/def ::port pos-int?)
|
|
|
|
|
(s/def ::db-spec-map (s/keys :req-un [::dbtype ::dbname]
|
|
|
|
|
:opt-un [::classname
|
|
|
|
|
::user ::password
|
2019-07-11 22:43:49 +00:00
|
|
|
::host ::port
|
|
|
|
|
::dbname-separator
|
|
|
|
|
::host-prefix]))
|
2020-03-17 17:28:40 +00:00
|
|
|
(defn jdbc-url-format?
|
|
|
|
|
"JDBC URLs must begin with `jdbc:` followed by the `dbtype` and
|
|
|
|
|
a second colon. Note: `clojure.java.jdbc` incorrectly allowed
|
|
|
|
|
`jdbc:` to be omitted at the beginning of a JDBC URL."
|
|
|
|
|
[url]
|
|
|
|
|
(re-find #"^jdbc:[^:]+:" url))
|
|
|
|
|
(s/def ::jdbcUrl (s/and string? jdbc-url-format?))
|
|
|
|
|
(comment
|
|
|
|
|
(s/explain-data ::jdbcUrl "jdbc:somedb://some-host/dbname")
|
|
|
|
|
(s/explain-data ::jdbcUrl "somedb://some-host/dbname"))
|
2019-09-28 05:22:23 +00:00
|
|
|
(s/def ::jdbc-url-map (s/keys :req-un [::jdbcUrl]))
|
2019-05-22 04:45:05 +00:00
|
|
|
|
|
|
|
|
(s/def ::connection #(instance? Connection %))
|
|
|
|
|
(s/def ::datasource #(instance? DataSource %))
|
|
|
|
|
(s/def ::prepared-statement #(instance? PreparedStatement %))
|
2019-12-20 23:45:22 +00:00
|
|
|
(s/def ::statement #(instance? Statement %))
|
2019-05-22 04:45:05 +00:00
|
|
|
|
2019-09-28 05:22:23 +00:00
|
|
|
(s/def ::db-spec (s/or :db-spec ::db-spec-map
|
|
|
|
|
:jdbc-url ::jdbc-url-map
|
2020-03-17 17:28:40 +00:00
|
|
|
:string ::jdbcUrl
|
2019-09-28 05:22:23 +00:00
|
|
|
:ds ::datasource))
|
|
|
|
|
(s/def ::db-spec-or-jdbc (s/or :db-spec ::db-spec-map
|
|
|
|
|
:jdbc-url ::jdbc-url-map))
|
2019-05-22 04:45:05 +00:00
|
|
|
|
|
|
|
|
(s/def ::connectable any?)
|
|
|
|
|
(s/def ::key-map (s/map-of keyword? any?))
|
2019-07-03 01:50:25 +00:00
|
|
|
(s/def ::example-map (s/map-of keyword? any? :min-count 1))
|
2019-07-11 19:11:32 +00:00
|
|
|
|
2020-06-25 19:48:22 +00:00
|
|
|
;; can be a simple column name (keyword) or a pair of something and as alias
|
|
|
|
|
;; and that something can be a simple column name (keyword) or an arbitrary
|
|
|
|
|
;; expression (string) where we assume you know what you're doing
|
|
|
|
|
(s/def ::column-spec (s/or :column keyword?
|
|
|
|
|
:alias (s/and vector?
|
|
|
|
|
(s/cat :expr (s/or :col keyword?
|
|
|
|
|
:str string?)
|
|
|
|
|
:column keyword?))))
|
|
|
|
|
(s/def ::columns (s/coll-of ::column-spec :kind vector?))
|
|
|
|
|
|
2019-07-11 19:11:32 +00:00
|
|
|
(s/def ::order-by-col (s/or :col keyword?
|
|
|
|
|
:dir (s/cat :col keyword?
|
|
|
|
|
:dir #{:asc :desc})))
|
|
|
|
|
(s/def ::order-by (s/coll-of ::order-by-col :kind vector? :min-count 1))
|
|
|
|
|
(s/def ::opts-map (s/and (s/map-of keyword? any?)
|
2020-06-25 19:48:22 +00:00
|
|
|
(s/keys :opt-un [::columns ::order-by])))
|
2019-05-22 04:45:05 +00:00
|
|
|
|
2019-06-02 16:03:14 +00:00
|
|
|
(s/def ::transactable any?)
|
|
|
|
|
|
2019-05-22 04:45:05 +00:00
|
|
|
(s/def ::sql-params (s/and vector?
|
|
|
|
|
(s/cat :sql string?
|
|
|
|
|
:params (s/* any?))))
|
2019-07-10 19:11:26 +00:00
|
|
|
(s/def ::params (s/coll-of any? :kind sequential?))
|
|
|
|
|
|
|
|
|
|
(s/def ::batch-size pos-int?)
|
|
|
|
|
(s/def ::large boolean?)
|
|
|
|
|
(s/def ::batch-opts (s/keys :opt-un [::batch-size ::large]))
|
2019-05-22 04:45:05 +00:00
|
|
|
|
|
|
|
|
(s/fdef jdbc/get-datasource
|
|
|
|
|
:args (s/cat :spec ::db-spec))
|
|
|
|
|
|
|
|
|
|
(s/fdef jdbc/get-connection
|
|
|
|
|
:args (s/cat :spec ::db-spec
|
|
|
|
|
:opts (s/? ::opts-map)))
|
|
|
|
|
|
|
|
|
|
(s/fdef jdbc/prepare
|
|
|
|
|
:args (s/cat :connection ::connection
|
|
|
|
|
:sql-params ::sql-params
|
|
|
|
|
:opts (s/? ::opts-map)))
|
|
|
|
|
|
2019-05-22 23:22:14 +00:00
|
|
|
(s/fdef jdbc/plan
|
2019-12-20 23:45:22 +00:00
|
|
|
:args (s/alt :prepared (s/cat :stmt ::statement)
|
2019-05-22 04:45:05 +00:00
|
|
|
:sql (s/cat :connectable ::connectable
|
2019-11-15 00:15:52 +00:00
|
|
|
:sql-params (s/nilable ::sql-params)
|
2019-05-22 04:45:05 +00:00
|
|
|
:opts (s/? ::opts-map))))
|
|
|
|
|
|
|
|
|
|
(s/fdef jdbc/execute!
|
2019-12-20 23:45:22 +00:00
|
|
|
:args (s/alt :prepared (s/cat :stmt ::statement)
|
2019-05-22 04:45:05 +00:00
|
|
|
:sql (s/cat :connectable ::connectable
|
2019-11-15 00:15:52 +00:00
|
|
|
:sql-params (s/nilable ::sql-params)
|
2019-05-22 04:45:05 +00:00
|
|
|
:opts (s/? ::opts-map))))
|
|
|
|
|
|
|
|
|
|
(s/fdef jdbc/execute-one!
|
2019-12-20 23:45:22 +00:00
|
|
|
:args (s/alt :prepared (s/cat :stmt ::statement)
|
2019-05-22 04:45:05 +00:00
|
|
|
:sql (s/cat :connectable ::connectable
|
2019-11-15 00:15:52 +00:00
|
|
|
:sql-params (s/nilable ::sql-params)
|
2019-05-22 04:45:05 +00:00
|
|
|
:opts (s/? ::opts-map))))
|
|
|
|
|
|
|
|
|
|
(s/fdef jdbc/transact
|
2019-06-02 16:03:14 +00:00
|
|
|
:args (s/cat :transactable ::transactable
|
2019-05-22 04:45:05 +00:00
|
|
|
:f fn?
|
|
|
|
|
:opts (s/? ::opts-map)))
|
|
|
|
|
|
2020-07-10 19:36:32 +00:00
|
|
|
(s/fdef jdbc/with-options
|
|
|
|
|
:args (s/cat :connectable ::connectable
|
|
|
|
|
:opts ::opts-map))
|
|
|
|
|
|
2019-05-22 04:45:05 +00:00
|
|
|
(s/fdef jdbc/with-transaction
|
|
|
|
|
:args (s/cat :binding (s/and vector?
|
|
|
|
|
(s/cat :sym simple-symbol?
|
2019-06-02 16:03:14 +00:00
|
|
|
:transactable ::transactable
|
2019-07-02 23:45:48 +00:00
|
|
|
:opts (s/? ::opts-map)))
|
2019-05-22 04:45:05 +00:00
|
|
|
:body (s/* any?)))
|
|
|
|
|
|
2019-07-18 18:01:55 +00:00
|
|
|
(s/fdef connection/->pool
|
2019-07-18 06:50:56 +00:00
|
|
|
:args (s/cat :clazz #(instance? Class %)
|
2019-09-28 05:22:23 +00:00
|
|
|
:db-spec ::db-spec-or-jdbc))
|
2019-07-18 06:50:56 +00:00
|
|
|
|
2020-06-25 02:27:32 +00:00
|
|
|
(s/fdef connection/component
|
|
|
|
|
:args (s/cat :clazz #(instance? Class %)
|
2020-07-10 07:35:20 +00:00
|
|
|
:db-spec ::db-spec-or-jdbc
|
2020-07-10 19:36:32 +00:00
|
|
|
:close-fn (s/? fn?)))
|
2020-06-25 02:27:32 +00:00
|
|
|
|
2019-07-10 19:11:26 +00:00
|
|
|
(s/fdef prepare/execute-batch!
|
|
|
|
|
:args (s/cat :ps ::prepared-statement
|
|
|
|
|
:param-groups (s/coll-of ::params :kind sequential?)
|
|
|
|
|
:opts (s/? ::batch-opts)))
|
|
|
|
|
|
|
|
|
|
(s/fdef prepare/set-parameters
|
|
|
|
|
:args (s/cat :ps ::prepared-statement
|
|
|
|
|
:params ::params))
|
|
|
|
|
|
2020-07-10 19:36:32 +00:00
|
|
|
(s/fdef prepare/statement
|
|
|
|
|
:args (s/cat :connection ::connection
|
|
|
|
|
:opts (s/? ::opts-map)))
|
|
|
|
|
|
2019-05-22 04:45:05 +00:00
|
|
|
(s/fdef sql/insert!
|
|
|
|
|
:args (s/cat :connectable ::connectable
|
|
|
|
|
:table keyword?
|
|
|
|
|
:key-map ::key-map
|
|
|
|
|
:opts (s/? ::opts-map)))
|
|
|
|
|
|
|
|
|
|
(s/fdef sql/insert-multi!
|
|
|
|
|
:args (s/and (s/cat :connectable ::connectable
|
|
|
|
|
:table keyword?
|
2019-07-11 19:52:36 +00:00
|
|
|
:cols (s/coll-of keyword?
|
|
|
|
|
:kind sequential?
|
|
|
|
|
:min-count 1)
|
|
|
|
|
:rows (s/coll-of (s/coll-of any? :kind sequential?)
|
|
|
|
|
:kind sequential?)
|
2019-05-22 04:45:05 +00:00
|
|
|
:opts (s/? ::opts-map))
|
|
|
|
|
#(apply = (count (:cols %))
|
|
|
|
|
(map count (:rows %)))))
|
|
|
|
|
|
|
|
|
|
(s/fdef sql/query
|
|
|
|
|
:args (s/cat :connectable ::connectable
|
|
|
|
|
:sql-params ::sql-params
|
|
|
|
|
:opts (s/? ::opts-map)))
|
|
|
|
|
|
|
|
|
|
(s/fdef sql/find-by-keys
|
|
|
|
|
:args (s/cat :connectable ::connectable
|
|
|
|
|
:table keyword?
|
2019-07-03 01:50:25 +00:00
|
|
|
:key-map (s/or :example ::example-map
|
2020-06-25 02:27:32 +00:00
|
|
|
:where ::sql-params
|
|
|
|
|
:all #{:all})
|
2019-05-22 04:45:05 +00:00
|
|
|
:opts (s/? ::opts-map)))
|
|
|
|
|
|
|
|
|
|
(s/fdef sql/get-by-id
|
|
|
|
|
:args (s/alt :with-id (s/cat :connectable ::connectable
|
|
|
|
|
:table keyword?
|
|
|
|
|
:pk any?
|
|
|
|
|
:opts (s/? ::opts-map))
|
|
|
|
|
:pk-name (s/cat :connectable ::connectable
|
|
|
|
|
:table keyword?
|
|
|
|
|
:pk any?
|
|
|
|
|
:pk-name keyword?
|
|
|
|
|
:opts ::opts-map)))
|
|
|
|
|
|
|
|
|
|
(s/fdef sql/update!
|
|
|
|
|
:args (s/cat :connectable ::connectable
|
|
|
|
|
:table keyword?
|
|
|
|
|
:key-map ::key-map
|
2019-07-03 01:50:25 +00:00
|
|
|
:where-params (s/or :example ::example-map
|
2019-05-22 04:45:05 +00:00
|
|
|
:where ::sql-params)
|
|
|
|
|
:opts (s/? ::opts-map)))
|
|
|
|
|
|
|
|
|
|
(s/fdef sql/delete!
|
|
|
|
|
:args (s/cat :connectable ::connectable
|
|
|
|
|
:table keyword?
|
2019-07-03 01:50:25 +00:00
|
|
|
:where-params (s/or :example ::example-map
|
2019-05-22 04:45:05 +00:00
|
|
|
:where ::sql-params)
|
|
|
|
|
:opts (s/? ::opts-map)))
|
|
|
|
|
|
2019-09-14 20:32:34 +00:00
|
|
|
(def ^:private fns-with-specs
|
|
|
|
|
[`jdbc/get-datasource
|
|
|
|
|
`jdbc/get-connection
|
|
|
|
|
`jdbc/prepare
|
|
|
|
|
`jdbc/plan
|
|
|
|
|
`jdbc/execute!
|
|
|
|
|
`jdbc/execute-one!
|
|
|
|
|
`jdbc/transact
|
|
|
|
|
`jdbc/with-transaction
|
2020-07-10 19:36:32 +00:00
|
|
|
`jdbc/with-options
|
2019-09-14 20:32:34 +00:00
|
|
|
`connection/->pool
|
2020-07-10 19:36:32 +00:00
|
|
|
`connection/component
|
2019-09-14 20:32:34 +00:00
|
|
|
`prepare/execute-batch!
|
|
|
|
|
`prepare/set-parameters
|
2019-12-20 23:45:22 +00:00
|
|
|
`prepare/statement
|
2019-09-14 20:32:34 +00:00
|
|
|
`sql/insert!
|
|
|
|
|
`sql/insert-multi!
|
|
|
|
|
`sql/query
|
|
|
|
|
`sql/find-by-keys
|
|
|
|
|
`sql/get-by-id
|
|
|
|
|
`sql/update!
|
|
|
|
|
`sql/delete!])
|
|
|
|
|
|
2019-05-22 04:45:05 +00:00
|
|
|
(defn instrument []
|
2019-09-14 20:32:34 +00:00
|
|
|
(st/instrument fns-with-specs))
|
2019-09-14 19:51:46 +00:00
|
|
|
|
|
|
|
|
(defn unstrument []
|
2019-09-14 20:32:34 +00:00
|
|
|
(st/unstrument fns-with-specs))
|