diff --git a/README.md b/README.md index b22d4be..0db5ad7 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ In addition, there are API functions to create `PreparedStatement`s (`prepare`) Since `next.jdbc` uses raw Java JDBC types, you can use `with-open` directly to reuse connections and ensure they are cleaned up correctly: -``` +```clojure (let [my-datasource (get-datasource {:dbtype "..." :dbname "..." ...})] (with-open [connection (get-connection my-datasource)] (execute! connection [...]) @@ -61,13 +61,13 @@ In addition, convenience functions -- "syntactic sugar" -- are provided to inser ## More Detailed Documentation -* [Getting Started](https://github.com/seancorfield/next-jdbc/blob/master/doc/getting_started.md) -* [Friendly SQL Functions](https://github.com/seancorfield/next-jdbc/blob/master/doc/friendly_sql_fns.md) -* [Row and Result Set Builders](https://github.com/seancorfield/next-jdbc/blob/master/doc/rs_builders.md) -* [Prepared Statements](https://github.com/seancorfield/next-jdbc/blob/master/doc/prepared_stmt.md) -* [Transactions](https://github.com/seancorfield/next-jdbc/blob/master/doc/transactions.md) -* [All The Options](https://github.com/seancorfield/next-jdbc/blob/master/doc/options.md) -* [Migration from `clojure.java.jdbc`](https://github.com/seancorfield/next-jdbc/blob/master/doc/differences.md) +* [Getting Started](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT/doc/getting-started) +* [Friendly SQL Functions](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT/doc/getting-started/friendly-sql-fns) +* [Row and Result Set Builders](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT/doc/getting-started/rs-builders) +* [Prepared Statements](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT/doc/getting-started/prepared-stmt) +* [Transactions](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT/doc/getting-started/transactions) +* [All The Options](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT/doc/options) +* [Migration from `clojure.java.jdbc`](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT/doc/differences) ## License diff --git a/doc/differences.md b/doc/differences.md index 2cfd0e4..e8b6526 100644 --- a/doc/differences.md +++ b/doc/differences.md @@ -22,14 +22,14 @@ If you used `:as-arrays? true`, you will need to use a `:gen-fn` option of `next * `get-connection` -- overlaps with `clojure.java.jdbc` (and returns a `java.sql.Connection`) but accepts only a subset of the options (`:dbtype`/`:dbname` hash map, `String` JDBC URI); `clojure.java.jdbc/get-connection` accepts `{:datasource ds}` whereas `next.jdbc/get-connection` accepts the `javax.sql.DataSource` object directly, * `prepare` -- somewhat similar to `clojure.java.jdbc/prepare-statement` but it accepts a vector of SQL and parameters (compared to just a raw SQL string), * `reducible!` -- somewhat similar to `clojure.java.jdbc/reducible-query` but accepts arbitrary SQL statements for execution, -* `execute!` -- has no equivalent in `clojure.java.jdbc`, -* `execute-one!` -- has no equivant in `clojure.java.jdbc`, +* `execute!` -- has no direct equivalent in `clojure.java.jdbc` (but it can replace most uses of both `query` and `db-do-commands`), +* `execute-one!` -- has no equivalent in `clojure.java.jdbc` (but it can replace most uses of `query` that currently use `:result-set-fn first`), * `transact` -- similar to `clojure.java.jdbc/db-transaction*`, * `with-transaction` -- similar to `clojure.java.jdbc/with-db-transaction`. If you were using a bare `db-spec` hash map with `:dbtype`/`:dbname`, or a JDBC URI string everywhere, that should mostly work with `next.jdbc` since most functions accept a "connectable", but it would be better to create a datasource first, and then pass that around. -If you were already creating a pooled connection datasource, as a `{:datasource ds}` hashmap, then passing `(:datasource db-spec)` to the `next.jdbc` functions is the simplest migration path. +If you were already creating `db-spec` as a pooled connection datasource -- a `{:datasource ds}` hashmap -- then passing `(:datasource db-spec)` to the `next.jdbc` functions is the simplest migration path. If you were using other forms of the `db-spec` hash map, you'll need to adjust to one of the three modes above, since those are the only ones supported in `next.jdbc`. @@ -45,7 +45,7 @@ The `next.jdbc.sql` namespace contains several functions with similarities to `c If you are using `:identifiers` and/or `:entities`, you will need to change to appropriate `:gen-fn` and/or `:table-fn`/`:column-fn` options. For the latter, instead of the `quoted` function, there is `next.jdbc.quoted` which contains functions for the common quoting strategies. -If you are using `:result-set-fn` and/or `:row-fn`, you will need to change to explicit calls (to the result set function, or to `map` the row function), or to use the `reducible!` approach with `reduce` or various transducing functions. Note: this means that result sets are never exposed lazily in `next.jdbc` -- in `clojure.java.jdbc` you had to be careful that your `:result-set-fn` was eager, but in `next.jdbc` you either reduce the result set eagerly (via `reducible!`) or you get a fully-realized result set data structure back (from `execute!` and `execute-one!`). As with `clojure.java.jdbc` however, you can still stream result sets from the database and process them via reduction (was `reducible-query`, now `reducible!`). +If you are using `:result-set-fn` and/or `:row-fn`, you will need to change to explicit calls (to the result set function, or to `map` the row function), or to use the `reducible!` approach with `reduce` or various transducing functions. Note: this means that result sets are never exposed lazily in `next.jdbc` -- in `clojure.java.jdbc` you had to be careful that your `:result-set-fn` was eager, but in `next.jdbc` you either reduce the result set eagerly (via `reducible!`) or you get a fully-realized result set data structure back (from `execute!` and `execute-one!`). As with `clojure.java.jdbc` however, you can still stream result sets from the database and process them via reduction (was `reducible-query`, now `reducible!`). Remember that you can terminate a reduction early by using the `reduced` function to wrap the final value you produce. ## Further Minor differences diff --git a/src/next/jdbc.clj b/src/next/jdbc.clj index 936bdcb..cecce2f 100644 --- a/src/next/jdbc.clj +++ b/src/next/jdbc.clj @@ -3,44 +3,45 @@ (ns next.jdbc "The public API of the next generation java.jdbc library. - The basic building blocks are the java.sql/javax.sql classes: - * DataSource -- something to get connections from, - * Connection -- an active connection to the database, - * PreparedStatement -- SQL and parameters combined, from a connection, + The basic building blocks are the `java.sql`/`javax.sql` classes: + * `DataSource` -- something to get connections from, + * `Connection` -- an active connection to the database, + * `PreparedStatement` -- SQL and parameters combined, from a connection, and the following functions and a macro: - * reducible! -- given a connectable and SQL + parameters or a statement, + * `reducible!` -- given a connectable and SQL + parameters or a statement, return a reducible that, when reduced will execute the SQL and consume - the ResultSet produced, - * execute! -- given a connectable and SQL + parameters or a statement, - execute the SQL, consume the ResultSet produced, and return a vector + the `ResultSet` produced, + * `execute!` -- given a connectable and SQL + parameters or a statement, + execute the SQL, consume the `ResultSet` produced, and return a vector of hash maps representing the rows (@1); this can be datafied to allow navigation of foreign keys into other tables (either by convention or via a schema definition), - * execute-one! -- given a connectable and SQL + parameters or a statement, - execute the SQL, consume the first row of the ResultSet produced, and + * `execute-one!` -- given a connectable and SQL + parameters or a statement, + execute the SQL, consume the first row of the `ResultSet` produced, and return a hash map representing that row; this can be datafied to allow navigation of foreign keys into other tables (either by convention or via a schema definition), - * prepare -- given a Connection and SQL + parameters, construct a new - PreparedStatement; in general this should be used with `with-open`, - * transact -- the functional implementation of `with-transaction`, - * with-transaction -- execute a series of SQL operations within a transaction. + * `prepare` -- given a `Connection` and SQL + parameters, construct a new + `PreparedStatement`; in general this should be used with `with-open`, + * `transact` -- the functional implementation of `with-transaction`, + * `with-transaction` -- execute a series of SQL operations within a transaction. @1 result sets are built, by default, as vectors of hash maps, containing qualified keywords as column names, but the row builder and result set builder machinery is open and alternatives are provided to produce unqualified keywords as column names, and to produce a vector the - column names followed by vectors of column values for each row. + column names followed by vectors of column values for each row, and + lower-case variants of each. - The following options are supported wherever a PreparedStatement is created: - * :concurrency -- :read-only, :updatable, - * :cursors -- :close, :hold - * :fetch-size -- the fetch size value, - * :max-rows -- the maximum number of rows to return, - * :result-type -- :forward-only, :scroll-insensitive, :scroll-sensitive, - * :return-keys -- either true or a vector of key names to return, - * :timeout -- the query timeout." + The following options are supported wherever a `PreparedStatement` is created: + * `:concurrency` -- `:read-only`, `:updatable`, + * `:cursors` -- `:close`, `:hold` + * `:fetch-size` -- the fetch size value, + * `:max-rows` -- the maximum number of rows to return, + * `:result-type` -- `:forward-only`, `:scroll-insensitive`, `:scroll-sensitive`, + * `:return-keys` -- either `true` or a vector of key names to return, + * `:timeout` -- the query timeout." (:require [next.jdbc.connection] ; used to extend protocols [next.jdbc.prepare] ; used to extend protocols [next.jdbc.protocols :as p] @@ -50,58 +51,60 @@ (set! *warn-on-reflection* true) (defn get-datasource - "Given some sort of specification of a database, return a DataSource. + "Given some sort of specification of a database, return a `DataSource`. A specification can be a JDBC URL string (which is passed to the JDBC driver as-is), or a hash map. For the hash map, these keys are required: - * :dbtype -- a string indicating the type of the database - * :dbname -- a string indicating the name of the database to be used + * `:dbtype` -- a string indicating the type of the database + * `:dbname` -- a string indicating the name of the database to be used The following optional keys are commonly used: - * :user -- the username to authenticate with - * :password -- the password to authenticate with - * :host -- the hostname or IP address of the database (default: 127.0.0.1) - * :port -- the port for the database connection (the default is database- + * `:user` -- the username to authenticate with + * `:password` -- the password to authenticate with + * `:host` -- the hostname or IP address of the database (default: `127.0.0.1`) + * `:port` -- the port for the database connection (the default is database- specific -- see below) - * :classname -- if you need to override the default for the :dbtype + * `:classname` -- if you need to override the default for the `:dbtype` (or you want to use a database that next.jdbc does not know about!) Any additional options provided will be passed to the JDBC driver's - .getConnection call as a java.util.Properties structure. + `.getConnection` call as a `java.util.Properties` structure. Database types supported, and their defaults: - * derby -- org.apache.derby.jdbc.EmbeddedDriver -- also pass :create true + * `derby` -- `org.apache.derby.jdbc.EmbeddedDriver` -- also pass `:create true` if you want the database to be automatically created - * h2 -- org.h2.Driver -- for an on-disk database - * h2:mem -- org.h2.Driver -- for an in-memory database - * hsqldb, hsql -- org.hsqldb.jdbcDriver - * jtds:sqlserver, jtds -- net.sourceforge.jtds.jdbc.Driver -- 1433 - * mysql -- com.mysql.cj.jdbc.Driver, com.mysql.jdbc.Driver -- 3306 - * oracle:oci -- oracle.jdbc.OracleDriver -- 1521 - * oracle:thin, oracle -- oracle.jdbc.OracleDriver -- 1521 - * oracle:sid -- oracle.jdbc.OracleDriver -- 1521 -- uses the legacy : - separator for the database name but otherwise behaves like oracle:thin - * postgresql, postgres -- org.postgresql.Driver -- 5432 - * pgsql -- com.impossibl.postgres.jdbc.PGDriver -- no default port - * redshift -- com.amazon.redshift.jdbc.Driver -- no default port - * sqlite -- org.sqlite.JDBC - * sqlserver, mssql -- com.microsoft.sqlserver.jdbc.SQLServerDriver -- 1433" + * `h2` -- `org.h2.Driver` -- for an on-disk database + * `h2:mem` -- `org.h2.Driver` -- for an in-memory database + * `hsqldb`, `hsql` -- `org.hsqldb.jdbcDriver` + * `jtds:sqlserver`, `jtds` -- `net.sourceforge.jtds.jdbc.Driver` -- `1433` + * `mysql` -- `com.mysql.cj.jdbc.Driver`, `com.mysql.jdbc.Driver` -- `3306` + * `oracle:oci` -- `oracle.jdbc.OracleDriver` -- `1521` + * `oracle:thin`, `oracle` -- `oracle.jdbc.OracleDriver` -- `1521` + * `oracle:sid` -- `oracle.jdbc.OracleDriver` -- `1521` -- uses the legacy `:` + separator for the database name but otherwise behaves like `oracle:thin` + * `postgresql`, `postgres` -- `org.postgresql.Driver` -- `5432` + * `pgsql` -- `com.impossibl.postgres.jdbc.PGDriver` -- no default port + * `redshift` -- `com.amazon.redshift.jdbc.Driver` -- no default port + * `sqlite` -- `org.sqlite.JDBC` + * `sqlserver`, `mssql` -- `com.microsoft.sqlserver.jdbc.SQLServerDriver` -- `1433`" [spec] (p/get-datasource spec)) (defn get-connection - "Given some sort of specification of a database, return a new Connection. + "Given some sort of specification of a database, return a new `Connection`. - In general, this should be used via with-open: + In general, this should be used via `with-open`: +```clojure (with-open [con (get-connection spec opts)] (run-some-ops con)) +``` - If you call get-connection on a DataSource, it just calls .getConnection - and applies the :auto-commit and/or :read-only options, if provided. + If you call `get-connection` on a `DataSource`, it just calls `.getConnection` + and applies the `:auto-commit` and/or `:read-only` options, if provided. - If you call get-connection on anything else, it will call get-datasource - first to try to get a DataSource, and then call get-connection on that." + If you call `get-connection` on anything else, it will call `get-datasource` + first to try to get a `DataSource`, and then call `get-connection` on that." ([spec] (p/get-connection spec {})) ([spec opts] @@ -109,12 +112,14 @@ (defn prepare "Given a connection to a database, and a vector containing SQL and any - parameters it needs, return a new PreparedStatement. + parameters it needs, return a new `PreparedStatement`. - In general, this should be used via with-open: + In general, this should be used via `with-open`: +```clojure (with-open [stmt (prepare spec sql-params opts)] (run-some-ops stmt)) +``` See the list of options above (in the namespace docstring) for what can be passed to prepare." @@ -128,8 +133,8 @@ Returns a reducible that, when reduced, runs the SQL and yields the result. - Can be called on a PreparedStatement, a Connection, or something that can - produce a Connection via a DataSource." + Can be called on a `PreparedStatement`, a `Connection`, or something that can + produce a `Connection` via a `DataSource`." ([stmt] (p/-execute stmt [] {})) ([connectable sql-params] @@ -140,10 +145,10 @@ (defn execute! "General SQL execution function. - Invokes 'reducible!' and then reduces that into a vector of hash maps. + Returns a fully-realized result set. - Can be called on a PreparedStatement, a Connection, or something that can - produce a Connection via a DataSource." + Can be called on a `PreparedStatement`, a `Connection`, or something that can + produce a `Connection` via a `DataSource`." ([stmt] (p/-execute-all stmt [] {})) ([connectable sql-params] @@ -154,8 +159,8 @@ (defn execute-one! "General SQL execution function that returns just the first row of a result. - Can be called on a PreparedStatement, a Connection, or something that can - produce a Connection via a DataSource." + Can be called on a `PreparedStatement`, a `Connection`, or something that can + produce a `Connection` via a `DataSource`." ([stmt] (p/-execute-one stmt [] {})) ([connectable sql-params] @@ -164,7 +169,7 @@ (p/-execute-one connectable sql-params opts))) (defn transact - "Given a connectable object and a function (taking a Connection), + "Given a connectable object and a function (taking a `Connection`), execute the function on a new connection in a transactional manner. See `with-transaction` for supported options." @@ -174,14 +179,14 @@ (p/-transact connectable f opts))) (defmacro with-transaction - "Given a connectable object, gets a new connection and binds it to 'sym', - then executes the 'body' in that context, committing any changes if the body + "Given a connectable object, gets a new connection and binds it to `sym`, + then executes the `body` in that context, committing any changes if the body completes successfully, otherwise rolling back any changes made. The options map supports: - * isolation -- :none, :read-committed, :read-uncommitted, :repeatable-read, - :serializable, - * :read-only -- true / false, - * :rollback-only -- true / false." + * `:isolation` -- `:none`, `:read-committed`, `:read-uncommitted`, + `:repeatable-read`, `:serializable`, + * `:read-only` -- `true` / `false`, + * `:rollback-only` -- `true` / `false`." [[sym connectable opts] & body] `(transact ~connectable (^{:once true} fn* [~sym] ~@body) ~opts)) diff --git a/src/next/jdbc/connection.clj b/src/next/jdbc/connection.clj index def7717..8f65165 100644 --- a/src/next/jdbc/connection.clj +++ b/src/next/jdbc/connection.clj @@ -1,7 +1,7 @@ ;; copyright (c) 2018-2019 Sean Corfield, all rights reserved (ns next.jdbc.connection - "Standard implementations of get-datasource and get-connection." + "Standard implementations of `get-datasource` and `get-connection`." (:require [next.jdbc.protocols :as p]) (:import (java.sql Connection DriverManager) (javax.sql DataSource) @@ -10,9 +10,9 @@ (set! *warn-on-reflection* true) (def ^:private classnames - "Map of subprotocols to classnames. dbtype specifies one of these keys. + "Map of subprotocols to classnames. `:dbtype` specifies one of these keys. - The subprotocols map below provides aliases for dbtype. + 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 @@ -33,7 +33,7 @@ "sqlserver" "com.microsoft.sqlserver.jdbc.SQLServerDriver"}) (def ^:private aliases - "Map of schemes to subprotocols. Used to provide aliases for dbtype." + "Map of schemes to subprotocols. Used to provide aliases for `:dbtype`." {"hsql" "hsqldb" "jtds" "jtds:sqlserver" "mssql" "sqlserver" @@ -43,7 +43,7 @@ (def ^:private host-prefixes "Map of subprotocols to non-standard host-prefixes. - Anything not listed is assumed to use //." + Anything not listed is assumed to use `//`." {"oracle:oci" "@" "oracle:thin" "@"}) @@ -58,14 +58,13 @@ "sqlserver" 1433}) (def ^:private dbname-separators - "Map of schemes to separators. The default is / but a couple are different." + "Map of schemes to separators. The default is `/` but a couple are different." {"mssql" ";DATABASENAME=" "sqlserver" ";DATABASENAME=" "oracle:sid" ":"}) (defn- ^Properties as-properties - "Convert any seq of pairs to a java.util.Properties instance. - Uses as-sql-name to convert both keys and values into strings." + "Convert any seq of pairs to a `java.util.Properties` instance." [m] (let [p (Properties.)] (doseq [[k v] m] @@ -73,8 +72,8 @@ p)) (defn- get-driver-connection - "Common logic for loading the DriverManager and the designed JDBC driver - class and obtaining the appropriate Connection object." + "Common logic for loading the `DriverManager` and the designed JDBC driver + class and obtaining the appropriate `Connection` object." [url etc] ;; force DriverManager to be loaded (DriverManager/getLoginTimeout) @@ -130,7 +129,7 @@ [s {}]) (defn- url+etc->datasource - "Given a JDBC URL and a map of options, return a DataSource that can be + "Given a JDBC URL and a map of options, return a `DataSource` that can be used to obtain a new database connection." [[url etc]] (reify DataSource @@ -143,13 +142,13 @@ :password password))))) (defn- make-connection - "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. The options supported are: - * :auto-commit -- whether the connection should be set to auto-commit or not; - without this option, the defaut is true -- connections will auto-commit, - * :read-only -- whether the connection should be set to read-only mode." + * `:auto-commit` -- whether the connection should be set to auto-commit or not; + without this option, the defaut is `true` -- connections will auto-commit, + * `:read-only` -- whether the connection should be set to read-only mode." ^Connection [^DataSource datasource opts] (let [^Connection connection (.getConnection datasource)] diff --git a/src/next/jdbc/prepare.clj b/src/next/jdbc/prepare.clj index f4f5628..7c77e84 100644 --- a/src/next/jdbc/prepare.clj +++ b/src/next/jdbc/prepare.clj @@ -1,13 +1,13 @@ ;; copyright (c) 2018-2019 Sean Corfield, all rights reserved (ns next.jdbc.prepare - "Mostly an implementation namespace for how PreparedStatement objects are + "Mostly an implementation namespace for how `PreparedStatement objects` are created by the next generation java.jdbc library. - set-parameters is public and may be useful if you have a PreparedStatement + `set-parameters` is public and may be useful if you have a `PreparedStatement` that you wish to reuse and (re)set the parameters on it. - Defines the SettableParameter protocol for converting Clojure values + Defines the `SettableParameter` protocol for converting Clojure values to database-specific values." (:require [next.jdbc.protocols :as p]) (:import (java.sql Connection @@ -20,8 +20,8 @@ (defprotocol SettableParameter :extend-via-metadata true "Protocol for setting SQL parameters in statement objects, which can convert from Clojure values. The default implementation just - calls .setObject on the parameter value. It can be extended to use other - methods of PreparedStatement to convert and set parameter values." + calls `.setObject` on the parameter value. It can be extended to use other + methods of `PreparedStatement` to convert and set parameter values." (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.")) @@ -36,8 +36,8 @@ (.setObject s i nil))) (defn set-parameters - "Given a PreparedStatement and a vector of parameter values, update the - PreparedStatement with those parameters and return it." + "Given a `PreparedStatement` and a vector of parameter values, update the + `PreparedStatement` with those parameters and return it." ^java.sql.PreparedStatement [^PreparedStatement ps params] (when (seq params) @@ -48,19 +48,19 @@ ps) (def ^{:private true - :doc "Map friendly :concurrency values to ResultSet constants."} + :doc "Map friendly `:concurrency` values to `ResultSet` constants."} result-set-concurrency {:read-only ResultSet/CONCUR_READ_ONLY :updatable ResultSet/CONCUR_UPDATABLE}) (def ^{:private true - :doc "Map friendly :cursors values to ResultSet constants."} + :doc "Map friendly `:cursors` values to `ResultSet` constants."} result-set-holdability {:hold ResultSet/HOLD_CURSORS_OVER_COMMIT :close ResultSet/CLOSE_CURSORS_AT_COMMIT}) (def ^{:private true - :doc "Map friendly :type values to ResultSet constants."} + :doc "Map friendly `:type` values to `ResultSet` constants."} result-set-type {:forward-only ResultSet/TYPE_FORWARD_ONLY :scroll-insensitive ResultSet/TYPE_SCROLL_INSENSITIVE @@ -71,8 +71,8 @@ (into-array String return-keys)) (defn create - "Given a connection, a SQL string, some parameters, and some options, - return a PreparedStatement representing that." + "Given a `Connection`, a SQL string, some parameters, and some options, + return a `PreparedStatement` representing that." ^java.sql.PreparedStatement [^Connection con ^String sql params {:keys [return-keys result-type concurrency cursors diff --git a/src/next/jdbc/protocols.clj b/src/next/jdbc/protocols.clj index 00b7e5c..c3ef2b5 100644 --- a/src/next/jdbc/protocols.clj +++ b/src/next/jdbc/protocols.clj @@ -3,52 +3,52 @@ (ns next.jdbc.protocols "This is the extensible core of the next generation java.jdbc library. - Sourceable protocol: - get-datasource -- turn something into a javax.sql.DataSource; implementations - are provided for strings, hash maps (db-spec structures), and also a - DataSource (which just returns itself). + `Sourceable` protocol: + `get-datasource` -- turn something into a `javax.sql.DataSource`; implementations + are provided for strings, hash maps (`db-spec` structures), and also a + `DataSource` (which just returns itself). - Connectable protocol: - get-connection -- create a new JDBC connection that should be closed when you - are finished with it; implementations are provided for DataSource and - Object, on the assumption that an Object can possibly be turned into a - DataSource. + `Connectable` protocol: + `get-connection` -- create a new JDBC connection that should be closed when you + are finished with it; implementations are provided for `DataSource`, + `PreparedStatement`, and `Object`, on the assumption that an `Object` + can possibly be turned into a `DataSource`. - Executable protocol: - -execute -- given SQL and parameters, produce a 'reducible' that, when - reduced, executes the SQL and produces a ResultSet that can be processed; - implementations are provided for Connection, DataSource, - PreparedStatement, and Object (on the assumption that an Object can be - turned into a DataSource and therefore used to get a Connection). + `Executable` protocol: + `-execute` -- given SQL and parameters, produce a 'reducible' that, when + reduced, executes the SQL and produces a `ResultSet` that can be processed; + implementations are provided for `Connection`, `DataSource`, + `PreparedStatement`, and `Object` (on the assumption that an `Object` can be + turned into a `DataSource` and therefore used to get a `Connection`). - -execute-one -- given SQL and parameters, executes the SQL and produces - the first row of the ResultSet as a datafiable hash map (by default); - implementations are provided for Connection, DataSource, - PreparedStatement, and Object (on the assumption that an Object can be - turned into a DataSource and therefore used to get a Connection). + `-execute-one` -- given SQL and parameters, executes the SQL and produces + the first row of the `ResultSet` as a datafiable hash map (by default); + implementations are provided for `Connection`, `DataSource`, + `PreparedStatement`, and `Object` (on the assumption that an `Object` can be + turned into a `DataSource` and therefore used to get a `Connection`). - -execute-all -- given SQL and parameters, executes the SQL and produces - either a vector of datafiable hash maps from the ResultSet (default) + `-execute-all` -- given SQL and parameters, executes the SQL and produces + either a vector of datafiable hash maps from the `ResultSet` (default) or a vector of column names followed by vectors of row values; - implementations are provided for Connection, DataSource, - PreparedStatement, and Object (on the assumption that an Object can be - turned into a DataSource and therefore used to get a Connection). + implementations are provided for `Connection`, `DataSource`, + `PreparedStatement`, and `Object` (on the assumption that an `Object` can be + turned into a `DataSource` and therefore used to get a `Connection`). - Preparable protocol: - prepare -- given SQL and parameters, produce a PreparedStatement that can + `Preparable` protocol: + `prepare` -- given SQL and parameters, produce a `PreparedStatement` that can be executed (by -execute above); implementation is provided for - Connection. + `Connection` only. - Transactable protocol: - -transact -- given a function (presumably containing SQL operations), + `Transactable` protocol: + `-transact` -- given a function (presumably containing SQL operations), run the function inside a SQL transaction; implementations are provided - for Connection, DataSource, and Object (on the assumption that an Object - can be turned into a DataSource).") + for `Connection`, `DataSource`, and `Object` (on the assumption that an + `Object` can be turned into a `DataSource`).") (set! *warn-on-reflection* true) (defprotocol Sourceable :extend-via-metadata true - (get-datasource ^javax.sql.DataSource [this] "Turn this into a javax.sql.DataSource.")) + (get-datasource ^javax.sql.DataSource [this])) (defprotocol Connectable (get-connection ^java.lang.AutoCloseable [this opts])) (defprotocol Executable diff --git a/src/next/jdbc/quoted.clj b/src/next/jdbc/quoted.clj index 773e784..1e5cdc7 100644 --- a/src/next/jdbc/quoted.clj +++ b/src/next/jdbc/quoted.clj @@ -1,7 +1,7 @@ ;; copyright (c) 2019 Sean Corfield, all rights reserved (ns next.jdbc.quoted - "Provides functions for use with the :table-fn and :column-fn options + "Provides functions for use with the `:table-fn` and `:column-fn` options that define how SQL entities should be quoted in strings constructed from Clojure data." (:require [clojure.string :as str])) @@ -19,9 +19,11 @@ (defn schema "Given a quoting function, return a new quoting function that will process schema-qualified names by quoting each segment: - - (mysql :foo.bar) ;=> `foo.bar` - ((schema mysql) :foo.bar) ;=> `foo`.`bar`" +```clojure + (mysql (name :foo.bar)) ;=> `foo.bar` + ((schema mysql) (name :foo.bar)) ;=> `foo`.`bar` +``` +" [quoting] (fn [s] (->> (str/split s #"\.") diff --git a/src/next/jdbc/result_set.clj b/src/next/jdbc/result_set.clj index 5dadb13..e47febf 100644 --- a/src/next/jdbc/result_set.clj +++ b/src/next/jdbc/result_set.clj @@ -1,16 +1,16 @@ ;; copyright (c) 2018-2019 Sean Corfield, all rights reserved (ns next.jdbc.result-set - "An implementation of ResultSet handling functions. + "An implementation of `ResultSet` handling functions. Defines the following protocols: - * ReadableColumn -- to read column values by label or index - * RowBuilder -- for materializing a row - * ResultSetBuilder -- for materializing a result set - * DatafiableRow -- for turning a row into something datafiable + * `DatafiableRow` -- for turning a row into something datafiable + * `ReadableColumn` -- to read column values by label or index + * `RowBuilder` -- for materializing a row + * `ResultSetBuilder` -- for materializing a result set - Also provides the default implemenations for Executable and - the default datafy/nav behavior for rows from a result set." + Also provides the default implemenations for `Executable` and + the default `datafy`/`nav` behavior for rows from a result set." (:require [clojure.core.protocols :as core-p] [clojure.string :as str] [next.jdbc.prepare :as prepare] @@ -22,7 +22,7 @@ (set! *warn-on-reflection* true) (defn get-column-names - "Given ResultSetMetaData, return a vector of column names, each qualified by + "Given `ResultSetMetaData`, return a vector of column names, each qualified by the table from which it came." [^ResultSetMetaData rsmeta opts] (mapv (fn [^Integer i] (keyword (not-empty (.getTableName rsmeta i)) @@ -30,13 +30,13 @@ (range 1 (inc (.getColumnCount rsmeta))))) (defn get-unqualified-column-names - "Given ResultSetMetaData, return a vector of unqualified column names." + "Given `ResultSetMetaData`, return a vector of unqualified column names." [^ResultSetMetaData rsmeta opts] (mapv (fn [^Integer i] (keyword (.getColumnLabel rsmeta i))) (range 1 (inc (.getColumnCount rsmeta))))) (defn get-lower-column-names - "Given ResultSetMetaData, return a vector of lower-case column names, each + "Given `ResultSetMetaData`, return a vector of lower-case column names, each qualified by the table from which it came." [^ResultSetMetaData rsmeta opts] (mapv (fn [^Integer i] (keyword (some-> (.getTableName rsmeta i) @@ -47,15 +47,15 @@ (range 1 (inc (.getColumnCount rsmeta))))) (defn get-unqualified-lower-column-names - "Given ResultSetMetaData, return a vector of unqualified column names." + "Given `ResultSetMetaData`, return a vector of unqualified column names." [^ResultSetMetaData rsmeta opts] (mapv (fn [^Integer i] (keyword (str/lower-case (.getColumnLabel rsmeta i)))) (range 1 (inc (.getColumnCount rsmeta))))) (defprotocol ReadableColumn - "Protocol for reading objects from the java.sql.ResultSet. Default - implementations (for Object and nil) return the argument, and the - Boolean implementation ensures a canonicalized true/false value, + "Protocol for reading objects from the `java.sql.ResultSet`. Default + implementations (for `Object` and `nil`) return the argument, and the + `Boolean` implementation ensures a canonicalized `true`/`false` value, but it can be extended to provide custom behavior for special types." (read-column-by-label [val label] "Function for transforming values after reading them via a column label.") @@ -77,13 +77,13 @@ (defprotocol RowBuilder "Protocol for building rows in various representations: - ->row -- called once per row to create the basis of each row - column-count -- return the number of columns in each row - with-column -- called with the row and the index of the column to be added; - this is expected to read the column value from the ResultSet! - row! -- called once per row to finalize each row once it is complete + `->row` -- called once per row to create the basis of each row + `column-count` -- return the number of columns in each row + `with-column` -- called with the row and the index of the column to be added; + this is expected to read the column value from the `ResultSet`! + `row!` -- called once per row to finalize each row once it is complete - The default implementation for building hash maps: MapResultSetBuilder" + The default implementation for building hash maps: `MapResultSetBuilder`" (->row [_]) (column-count [_]) (with-column [_ row i]) @@ -91,12 +91,12 @@ (defprotocol ResultSetBuilder "Protocol for building result sets in various representations: - ->rs -- called to create the basis of the result set - with-row -- called with the result set and the row to be added - rs! -- called to finalize the result set once it is complete + `->rs` -- called to create the basis of the result set + `with-row` -- called with the result set and the row to be added + `rs!` -- called to finalize the result set once it is complete Default implementations for building vectors of hash maps and vectors - of column names and row values: MapResultSetBuilder & ArrayResultSetBuilder" + of column names and row values: `MapResultSetBuilder` & `ArrayResultSetBuilder`" (->rs [_]) (with-row [_ rs row]) (rs! [_ rs])) @@ -117,7 +117,7 @@ (rs! [this mrs] (persistent! mrs))) (defn as-maps - "Given a ResultSet and options, return a RowBuilder / ResultSetBuilder + "Given a `ResultSet` and options, return a `RowBuilder` / R`esultSetBuilder` that produces bare vectors of hash map rows." [^ResultSet rs opts] (let [rsmeta (.getMetaData rs) @@ -125,7 +125,7 @@ (->MapResultSetBuilder rs rsmeta cols))) (defn as-unqualified-maps - "Given a ResultSet and options, return a RowBuilder / ResultSetBuilder + "Given a `ResultSet` and options, return a `RowBuilder` / `ResultSetBuilder` that produces bare vectors of hash map rows, with simple keys." [^ResultSet rs opts] (let [rsmeta (.getMetaData rs) @@ -133,7 +133,7 @@ (->MapResultSetBuilder rs rsmeta cols))) (defn as-lower-maps - "Given a ResultSet and options, return a RowBuilder / ResultSetBuilder + "Given a `ResultSet` and options, return a `RowBuilder` / `ResultSetBuilder` that produces bare vectors of hash map rows, with lower-case keys." [^ResultSet rs opts] (let [rsmeta (.getMetaData rs) @@ -141,7 +141,7 @@ (->MapResultSetBuilder rs rsmeta cols))) (defn as-unqualified-lower-maps - "Given a ResultSet and options, return a RowBuilder / ResultSetBuilder + "Given a `ResultSet` and options, return a `RowBuilder` / `ResultSetBuilder` that produces bare vectors of hash map rows, with simple, lower-case keys." [^ResultSet rs opts] (let [rsmeta (.getMetaData rs) @@ -162,7 +162,7 @@ (rs! [this ars] (persistent! ars))) (defn as-arrays - "Given a ResultSet and options, return a RowBuilder / ResultSetBuilder + "Given a `ResultSet` and options, return a `RowBuilder` / `ResultSetBuilder` that produces a vector of column names followed by vectors of row values." [^ResultSet rs opts] (let [rsmeta (.getMetaData rs) @@ -170,7 +170,7 @@ (->ArrayResultSetBuilder rs rsmeta cols))) (defn as-unqualified-arrays - "Given a ResultSet and options, return a RowBuilder / ResultSetBuilder + "Given a `ResultSet` and options, return a `RowBuilder` / `ResultSetBuilder` that produces a vector of simple column names followed by vectors of row values." [^ResultSet rs opts] @@ -179,7 +179,7 @@ (->ArrayResultSetBuilder rs rsmeta cols))) (defn as-lower-arrays - "Given a ResultSet and options, return a RowBuilder / ResultSetBuilder + "Given a `ResultSet` and options, return a `RowBuilder` / `ResultSetBuilder` that produces a vector of lower-case column names followed by vectors of row values." [^ResultSet rs opts] @@ -188,7 +188,7 @@ (->ArrayResultSetBuilder rs rsmeta cols))) (defn as-unqualified-lower-arrays - "Given a ResultSet and options, return a RowBuilder / ResultSetBuilder + "Given a `ResultSet` and options, return a `RowBuilder` / `ResultSetBuilder` that produces a vector of simple, lower-case column names followed by vectors of row values." [^ResultSet rs opts] @@ -200,11 +200,11 @@ (defprotocol DatafiableRow "Given a connectable object, return a function that knows how to turn a row - into a datafiable object that can be 'nav'igated." + into a datafiable object that can be `nav`igated." (datafiable-row [this connectable opts])) (defn- row-builder - "Given a RowBuilder -- a row materialization strategy -- produce a fully + "Given a `RowBuilder` -- a row materialization strategy -- produce a fully materialized row from it." [gen] (->> (reduce (fn [r i] (with-column gen r i)) @@ -213,21 +213,21 @@ (row! gen))) (defn- mapify-result-set - "Given a result set, return an object that wraps the current row as a hash + "Given a `ResultSet`, return an object that wraps the current row as a hash map. Note that a result set is mutable and the current row will change behind this wrapper so operations need to be eager (and fairly limited). In particular, this does not satisfy `map?` because it does not implement - all of IPersistentMap. + all of `IPersistentMap`. - Supports ILookup (keywords are treated as strings). + Supports `ILookup` (keywords are treated as strings). - Supports Associative (again, keywords are treated as strings). If you assoc, + Supports `Associative` (again, keywords are treated as strings). If you `assoc`, a full row will be realized (via `row-builder` above). - Supports Seqable which realizes a full row of the data. + Supports `Seqable` which realizes a full row of the data. - Supports DatafiableRow (which realizes a full row of the data)." + Supports `DatafiableRow` (which realizes a full row of the data)." [^ResultSet rs opts] (let [gen (delay ((get opts :gen-fn as-maps) rs opts))] (reify @@ -279,7 +279,7 @@ {`core-p/datafy (navize-row connectable opts)}))) (defn- stmt->result-set - "Given a PreparedStatement and options, execute it and return a ResultSet + "Given a `PreparedStatement` and options, execute it and return a `ResultSet` if possible." ^ResultSet [^PreparedStatement stmt opts] @@ -291,12 +291,12 @@ (catch Exception _))))) (defn- reduce-stmt - "Execute the PreparedStatement, attempt to get either its ResultSet or - its generated keys (as a ResultSet), and reduce that using the supplied + "Execute the `PreparedStatement`, attempt to get either its `ResultSet` or + its generated keys (as a `ResultSet`), and reduce that using the supplied function and initial value. - If the statement yields neither a ResultSet nor generated keys, return - a hash map containing :next.jdbc/update-count and the number of rows + If the statement yields neither a `ResultSet` nor generated keys, return + a hash map containing `:next.jdbc/update-count` and the number of rows updated, with the supplied function and initial value applied." [^PreparedStatement stmt f init opts] (if-let [rs (stmt->result-set stmt opts)] @@ -424,7 +424,7 @@ (defn- default-schema "The default schema lookup rule for column names. - If a column name ends with _id or id, it is assumed to be a foreign key + If a column name ends with `_id` or `id`, it is assumed to be a foreign key into the table identified by the first part of the column name." [col] (let [[_ table] (re-find #"(?i)^(.+?)_?id$" (name col))] @@ -433,19 +433,19 @@ (defn- navize-row "Given a connectable object, return a function that knows how to turn a row - into a navigable object. + into a `nav`igable object. A `:schema` option can provide a map from qualified column names (`:/`) to tuples that indicate for which table they are a foreign key, the name of the key within that table, and (optionality) the cardinality of that relationship (`:many`, `:one`). - If no `:schema` item is provided for a column, the convention of
id or -
_id is used, and the assumption is that such columns are foreign keys - in the
portion of their name, the key is called `id`, and the - cardinality is :one. + If no `:schema` item is provided for a column, the convention of `
id` or + `
_id` is used, and the assumption is that such columns are foreign keys + in the `
` portion of their name, the key is called `id`, and the + cardinality is `:one`. - Rows are looked up using `-execute-all` or `-execute-one` and the `:table-fn` + Rows are looked up using `-execute-all` or `-execute-one`, and the `:table-fn` option, if provided, is applied to both the assumed table name and the assumed foreign key column name." [connectable opts] diff --git a/src/next/jdbc/sql.clj b/src/next/jdbc/sql.clj index ebe06af..7af1b7c 100644 --- a/src/next/jdbc/sql.clj +++ b/src/next/jdbc/sql.clj @@ -221,12 +221,12 @@ (execute! connectable (for-query table key-map opts) opts))) (defn get-by-id - "Syntactic sugar over execute-one! to make certain common queries easier. + "Syntactic sugar over `execute-one!` to make certain common queries easier. Given a connectable object, a table name, and a primary key value, returns a hash map of the first row that matches. - By default, the primary key is assumed to be 'id' but that can be overridden + By default, the primary key is assumed to be `id` but that can be overridden in the five-argument call." ([connectable table pk] (get-by-id connectable table pk :id {})) @@ -236,7 +236,7 @@ (execute-one! connectable (for-query table {pk-name pk} opts) opts))) (defn update! - "Syntactic sugar over execute-one! to make certain common updates easier. + "Syntactic sugar over `execute-one!` to make certain common updates easier. Given a connectable object, a table name, a hash map of columns and values to set, and either a hash map of columns and values to search on or a vector @@ -249,7 +249,7 @@ opts))) (defn delete! - "Syntactic sugar over execute-one! to make certain common deletes easier. + "Syntactic sugar over `execute-one!` to make certain common deletes easier. Given a connectable object, a table name, and either a hash map of columns and values to search on or a vector of a SQL where clause and parameters,