This commit is contained in:
Sean Corfield 2022-10-08 23:51:59 -07:00
parent a0be7d162d
commit dd040150fc
3 changed files with 89 additions and 15 deletions

View file

@ -3,6 +3,7 @@
Only accretive/fixative changes will be made from now on.
* 1.3.next in progress
* Fix [#229](https://github.com/seancorfield/next-jdbc/issues/229) by adding `next.jdbc.connect/uri->db-spec` which converts a URI string to a db-spec hash map; in addition, if `DriverManager/getConnection` fails, it assumes it was passed a URI instead of a JDBC URL, and retries after calling that function and then recreating the JDBC URL (which should have the effect of moving the embedded user/password credentials into the properties structure instead of the URL).
* Address [#228](https://github.com/seancorfield/next-jdbc/issues/228) by adding `PreparedStatement` caveat to the Oracle **Tips & Tricks** section.
* 1.3.834 -- 2022-09-23

View file

@ -132,21 +132,6 @@
:dbname-separator ":dsn="
:host :none}})
(defn- ^Properties as-properties
"Convert any seq of pairs to a `java.util.Properties` instance."
[m]
(let [p (Properties.)]
(doseq [[k v] m]
(.setProperty p (name k) (str v)))
p))
(defn- get-driver-connection
"Common logic for loading the designated JDBC driver class and
obtaining the appropriate `Connection` object."
[url timeout etc]
(when timeout (DriverManager/setLoginTimeout timeout))
(DriverManager/getConnection url (as-properties etc)))
(def ^:private driver-cache
"An optimization for repeated calls to get-datasource, or for get-connection
called on a db-spec hash map, so that we only try to load the classes once."
@ -359,6 +344,53 @@
[s]
[s {}])
(defn- ^Properties as-properties
"Convert any seq of pairs to a `java.util.Properties` instance."
[m]
(let [p (Properties.)]
(doseq [[k v] m]
(.setProperty p (name k) (str v)))
p))
(defn uri->db-spec
"clojure.java.jdbc (and some users out there) considered the URI format
to be an acceptable JDBC URL, i.e., with credentials embdedded in the string,
rather than as query parameters.
This function accepts a URI string, optionally prefixed with `jdbc:` and
returns a db-spec hash map."
[uri]
(let [{:keys [scheme userInfo host port path query]}
(j/from-java (java.net.URI. (str/replace uri #"^jdbc:" "")))
[user password] (when (seq userInfo) (str/split userInfo #":"))
properties (when (seq query)
(into {}
(map #(str/split % #"="))
(str/split query #"\&")))]
(cond-> (assoc properties
:dbtype scheme
:host host
:port port)
(seq path) (assoc :dbname (subs path 1))
user (assoc :user user)
password (assoc :password password))))
(defn- get-driver-connection
"Common logic for loading the designated JDBC driver class and
obtaining the appropriate `Connection` object."
[url timeout etc]
(when timeout (DriverManager/setLoginTimeout timeout))
(try
(DriverManager/getConnection url (as-properties etc))
(catch Exception e
(try
(let [db-spec (uri->db-spec url)
[url' etc'] (spec->url+etc db-spec)]
(DriverManager/getConnection url' (as-properties (merge etc' etc))))
(catch Exception _
;; if the fallback fails too, throw the original exception
(throw e))))))
(defn- url+etc->datasource
"Given a JDBC URL and a map of options, return a `DataSource` that can be
used to obtain a new database connection."

View file

@ -0,0 +1,41 @@
;; copyright (c) 2019-2021 Sean Corfield, all rights reserved
(ns next.jdbc.connection-string-test
"Tests for the main hash map spec to JDBC URL logic and the get-datasource
and get-connection protocol implementations.
At some point, the datasource/connection tests should probably be extended
to accept EDN specs from an external source (environment variables?)."
(:require [clojure.string :as str]
[clojure.test :refer [deftest is testing use-fixtures]]
[next.jdbc.connection :as c]
[next.jdbc.protocols :as p]
[next.jdbc.specs :as specs]
[next.jdbc.test-fixtures :refer [with-test-db db]]))
(set! *warn-on-reflection* true)
(use-fixtures :once with-test-db)
(specs/instrument)
(deftest test-uri-strings
(testing "datasource via String"
(let [db-spec (db)
db-spec (if (= "embedded-postgres" (:dbtype db-spec))
(assoc db-spec :dbtype "postgresql")
db-spec)
[url etc] (#'c/spec->url+etc db-spec)
{:keys [user password]} etc
etc (dissoc etc :user :password)
uri (-> url
;; strip jdbc: prefix for fun
(str/replace #"^jdbc:" "")
(str/replace #";" "?") ; for SQL Server tests
(str/replace #":sqlserver" "") ; for SQL Server tests
(cond-> (and user password)
(str/replace #"://" (str "://" user ":" password "@"))))
ds (p/get-datasource (assoc etc :jdbcUrl uri))]
(when (and user password)
(with-open [con (p/get-connection ds {})]
(is (instance? java.sql.Connection con)))))))