Fix #47 by refactoring how db-specs are built

This commit is contained in:
Sean Corfield 2019-07-14 21:46:59 -07:00
parent e0e209aa1c
commit 816fc7ce4b
2 changed files with 56 additions and 76 deletions

View file

@ -6,6 +6,7 @@ Only accretive/fixative changes will be made from now on.
The following changes have been committed to the **master** branch since the 1.0.1 release:
* Fix #47 by refactoring database specs to be a single hash map instead of pouring multiple maps into one.
* Fix #46 by allowing `:host` to be `:none` which tells `next.jdbc` to omit the host/port section of the JDBC URL, so that local databases can be used with `:dbtype`/`:classname` for database types that `next.jdbc` does not know. Also added `:dbname-separator` and `:host-prefix` to the "db-spec" to allow fine-grained control over how the JDBC URL is assembled.
* Fix #45 by adding [TimesTen](https://www.oracle.com/database/technologies/related/timesten.html) driver support.
* Fix #44 so that `insert-multi!` with an empty `rows` vector returns `[]`.

View file

@ -9,65 +9,7 @@
(set! *warn-on-reflection* true)
(def ^:private classnames
"Map of subprotocols to classnames. `:dbtype` specifies one of these keys.
The subprotocols map below provides aliases for `:dbtype`.
Most databases have just a single class name for their driver but we
support a sequence of class names to try in order to allow for drivers
that change their names over time (e.g., MySQL)."
{"derby" "org.apache.derby.jdbc.EmbeddedDriver"
"h2" "org.h2.Driver"
"h2:mem" "org.h2.Driver"
"hsqldb" "org.hsqldb.jdbcDriver"
"jtds:sqlserver" "net.sourceforge.jtds.jdbc.Driver"
"mysql" ["com.mysql.cj.jdbc.Driver"
"com.mysql.jdbc.Driver"]
"oracle:oci" "oracle.jdbc.OracleDriver"
"oracle:thin" "oracle.jdbc.OracleDriver"
"postgresql" "org.postgresql.Driver"
"pgsql" "com.impossibl.postgres.jdbc.PGDriver"
"redshift" "com.amazon.redshift.jdbc.Driver"
"sqlite" "org.sqlite.JDBC"
"sqlserver" "com.microsoft.sqlserver.jdbc.SQLServerDriver"
"timesten:client" "com.timesten.jdbc.TimesTenClientDriver"
"timesten:direct" "com.timesten.jdbc.TimesTenDriver"})
(def ^:private aliases
"Map of schemes to subprotocols. Used to provide aliases for `:dbtype`."
{"hsql" "hsqldb"
"jtds" "jtds:sqlserver"
"mssql" "sqlserver"
"oracle" "oracle:thin"
"oracle:sid" "oracle:thin"
"postgres" "postgresql"})
(def ^:private host-prefixes
"Map of subprotocols to non-standard host-prefixes.
Anything not listed is assumed to use `//`."
{"oracle:oci" "@"
"oracle:thin" "@"})
(def ^:private ports
"Map of subprotocols to ports."
{"jtds:sqlserver" 1433
"mysql" 3306
"oracle:oci" 1521
"oracle:sid" 1521
"oracle:thin" 1521
"postgresql" 5432
"sqlserver" 1433})
(def ^:private dbname-separators
"Map of schemes to separators. The default is `/` but a couple are different."
{"mssql" ";DATABASENAME="
"sqlserver" ";DATABASENAME="
"oracle:sid" ":"
"timesten:client" ":dsn="
"timesten:direct" ":dsn="})
(def dbtypes
(def ^:private dbtypes
"A map of all known database types (including aliases) to the class name(s)
and port that `next.jdbc` supports out of the box. Just for completeness,
this also includes the prefixes used in the JDBC string for the `:host`
@ -113,17 +55,54 @@
named class in order, until one succeeds. This allows for a given `:dbtype`
to be used with different versions of a JDBC driver, if the class name
has changed over time (such as with MySQL)."
(let [dbs (merge (zipmap (keys classnames) (keys classnames)) aliases)]
(reduce-kv (fn [m k v]
(assoc m
k
(cond-> {:classname (classnames v)
:host-prefix (host-prefixes v "//")
:dbname-separator (dbname-separators v "/")}
(ports v)
(assoc :port (ports v)))))
{}
dbs)))
{"derby" {:classname "org.apache.derby.jdbc.EmbeddedDriver"}
"h2" {:classname "org.h2.Driver"}
"h2:mem" {:classname "org.h2.Driver"}
"hsql" {:classname "org.hsqldb.jdbcDriver"
:alias-for "hsqldb"}
"hsqldb" {:classname "org.hsqldb.jdbcDriver"}
"jtds" {:classname "net.sourceforge.jtds.jdbc.Driver"
:port 1433
:alias-for "jtds:sqlserver"}
"jtds:sqlserver" {:classname "net.sourceforge.jtds.jdbc.Driver"
:port 1433}
"mssql" {:classname "com.microsoft.sqlserver.jdbc.SQLServerDriver"
:port 1433
:alias-for "sqlserver"
:dbname-separator ";DATABASENAME="}
"mysql" {:classname ["com.mysql.cj.jdbc.Driver"
"com.mysql.jdbc.Driver"]
:port 3306}
"oracle" {:classname "oracle.jdbc.OracleDriver"
:port 1521
:alias-for "oracle:thin"
:host-prefix "@"}
"oracle:oci" {:classname "oracle.jdbc.OracleDriver"
:port 1521
:host-prefix "@"}
"oracle:sid" {:classname "oracle.jdbc.OracleDriver"
:port 1521
:alias-for "oracle:thin"
:dbname-separator ":"
:host-prefix "@"}
"oracle:thin" {:classname "oracle.jdbc.OracleDriver"
:port 1521
:host-prefix "@"}
"postgres" {:classname "org.postgresql.Driver"
:port 5432
:alias-for "postgresql"}
"postgresql" {:classname "org.postgresql.Driver"
:port 5432}
"pgsql" {:classname "com.impossibl.postgres.jdbc.PGDriver"}
"redshift" {:classname "com.amazon.redshift.jdbc.Driver"}
"sqlite" {:classname "org.sqlite.JDBC"}
"sqlserver" {:classname "com.microsoft.sqlserver.jdbc.SQLServerDriver"
:port 1433
:dbname-separator ";DATABASENAME="}
"timesten:client" {:classname "com.timesten.jdbc.TimesTenClientDriver"
:dbname-separator ":dsn="}
"timesten:direct" {:classname "com.timesten.jdbc.TimesTenDriver"
:dbname-separator ":dsn="}})
(defn- ^Properties as-properties
"Convert any seq of pairs to a `java.util.Properties` instance."
@ -150,11 +129,11 @@
dbname-separator host-prefix]
:as db-spec}]
(let [;; allow aliases for dbtype
subprotocol (aliases dbtype dbtype)
subprotocol (-> dbtype dbtypes :alias-for (or dbtype))
host (or host "127.0.0.1")
port (or port (ports subprotocol))
db-sep (or dbname-separator (dbname-separators dbtype "/"))
local-sep (or dbname-separator (dbname-separators dbtype ":"))
port (or port (-> dbtype dbtypes :port))
db-sep (or dbname-separator (-> dbtype dbtypes :dbname-separator (or "/")))
local-sep (or dbname-separator (-> dbtype dbtypes :dbname-separator (or ":")))
url (cond (#{"derby" "hsqldb" "sqlite"} subprotocol)
(str "jdbc:" subprotocol local-sep dbname)
@ -177,7 +156,7 @@
:else
(str "jdbc:" subprotocol ":"
(or host-prefix (host-prefixes subprotocol "//"))
(or host-prefix (-> dbtype dbtypes :host-prefix (or "//")))
host
(when port (str ":" port))
db-sep dbname))
@ -185,7 +164,7 @@
:dbtype :dbname :host :port :classname
:dbname-separator :host-prefix)]
;; verify the datasource is loadable
(if-let [class-name (or classname (classnames subprotocol))]
(if-let [class-name (or classname (-> dbtype dbtypes :classname))]
(swap! driver-cache update class-name
#(if % %
(do