From 81be78731645dbc3b59c438b9ca45909bbd51e1e Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 1 Apr 2019 21:31:38 -0700 Subject: [PATCH] Improve docstrings throughout --- src/next/jdbc.clj | 79 +++++++++++++++++++++++++++++------ src/next/jdbc/connection.clj | 2 +- src/next/jdbc/sql.clj | 13 ++++++ src/next/jdbc/transaction.clj | 8 +++- 4 files changed, 87 insertions(+), 15 deletions(-) diff --git a/src/next/jdbc.clj b/src/next/jdbc.clj index 5e7c4d0..8825d13 100644 --- a/src/next/jdbc.clj +++ b/src/next/jdbc.clj @@ -6,7 +6,8 @@ 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, + * PreparedStatement -- SQL and parameters combined, from a connection + and the following two functions and a macro: * 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, @@ -22,7 +23,8 @@ The following options are supported generally: * :entities -- specify a function used to convert strings to SQL entity names - (to turn table and column names into appropriate SQL names), + (to turn table and column names into appropriate SQL names -- see the + next.jdbc.quoted namespace for the most common ones you might need), * :identifiers -- specify a function used to convert SQL entity (column) names to Clojure names (that are then turned into keywords), * :row-fn -- when consuming a ResultSet, apply this function to each row of @@ -46,7 +48,42 @@ (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 + + 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- + specific -- see below) + * :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. + + Database types supported, and their defaults: + * 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" [spec] (p/get-datasource spec)) @@ -56,25 +93,37 @@ In general, this should be used via with-open: (with-open [con (get-connection spec opts)] - (run-some-ops con))" + (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 anything else, it will call get-datasource + first to try to get a DataSource, and then call get-connection on that." [spec opts] (p/get-connection spec opts)) (defn prepare - "Given some sort of specification of a database, and a vector containing - SQL and any parameters it needs, return a new PreparedStatement. + "Given a connection to a database, and a vector containing SQL and any + parameters it needs, return a new PreparedStatement. In general, this should be used via with-open: (with-open [stmt (prepare spec sql-params opts)] - (run-some-ops stmt))" - [spec sql-params opts] - (p/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." + [connection sql-params opts] + (p/prepare connection sql-params opts)) (defn reducible! "General SQL execution function. - Returns a reducible that, when reduced, runs the SQL and yields the result." + 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." ([stmt] (p/-execute stmt [] {})) ([connectable sql-params & [opts]] (p/-execute connectable sql-params opts))) @@ -82,7 +131,10 @@ (defn execute! "General SQL execution function. - Invokes 'reducible!' and then reduces that into a vector of hash maps." + Invokes 'reducible!' and then reduces that into a vector of hash maps. + + Can be called on a PreparedStatement, a Connection, or something that can + produce a Connection via a DataSource." ([stmt] (rs/execute! stmt [] {})) ([connectable sql-params] @@ -93,7 +145,10 @@ (defn execute-one! "General SQL execution function that returns just the first row of a result. - Invokes 'reducible!' but immediately returns the first row." + Invokes 'reducible!' but immediately returns the first row. + + Can be called on a PreparedStatement, a Connection, or something that can + produce a Connection via a DataSource." ([stmt] (rs/execute-one! stmt [] {})) ([connectable sql-params] diff --git a/src/next/jdbc/connection.clj b/src/next/jdbc/connection.clj index d73dbb4..86959f4 100644 --- a/src/next/jdbc/connection.clj +++ b/src/next/jdbc/connection.clj @@ -64,7 +64,7 @@ "oracle:sid" ":"}) (defn- ^Properties as-properties - "Convert any seq of pairs to a java.utils.Properties instance. + "Convert any seq of pairs to a java.util.Properties instance. Uses as-sql-name to convert both keys and values into strings." [m] (let [p (Properties.)] diff --git a/src/next/jdbc/sql.clj b/src/next/jdbc/sql.clj index 4cc9e47..9a7761e 100644 --- a/src/next/jdbc/sql.clj +++ b/src/next/jdbc/sql.clj @@ -130,19 +130,32 @@ (comment (require '[next.jdbc.quoted :refer [mysql]]) (by-keys {:a nil :b 42 :c "s"} :where {}) + ;=> ["WHERE a IS NULL AND b = ? AND c = ?" 42 "s"] (as-keys {:a nil :b 42 :c "s"} {}) + ;=> a, b, c (as-? {:a nil :b 42 :c "s"} {}) + ;=> ?, ?, ? (for-query :user {:id 9} {:entities mysql}) + ;=> ["SELECT * FROM `user` WHERE `id` = ?" 9] (for-query :user {:id nil} {:entities mysql}) + ;=> ["SELECT * FROM `user` WHERE `id` IS NULL"] (for-query :user ["id = ? and opt is null" 9] {:entities mysql}) + ;=> ["SELECT * FROM `user` WHERE id = ? and opt is null" 9] (for-delete :user {:opt nil :id 9} {:entities mysql}) + ;=> ["DELETE FROM `user` WHERE `opt` IS NULL AND `id` = ?" 9] (for-delete :user ["id = ? and opt is null" 9] {:entities mysql}) + ;=> ["DELETE FROM `user` WHERE id = ? and opt is null" 9] (for-update :user {:status 42} {} {:entities mysql}) + ;=> ["UPDATE `user` SET `status` = ? WHERE " 42] (for-update :user {:status 42} {:id 9} {:entities mysql}) + ;=> ["UPDATE `user` SET `status` = ? WHERE `id` = ?" 42 9] (for-update :user {:status 42, :opt nil} ["id = ?" 9] {:entities mysql}) + ;=> ["UPDATE `user` SET `status` = ?, `opt` = ? WHERE id = ?" 42 nil 9] (for-insert :user {:id 9 :status 42 :opt nil} {:entities mysql}) + ;=> ["INSERT INTO `user` (`id`, `status`, `opt`) VALUES (?, ?, ?)" 9 42 nil] (for-insert-multi :user [:id :status] [[42 "hello"] [35 "world"] [64 "dollars"]] {:entities mysql})) + ;=> ["INSERT INTO `user` (`id`, `status`) VALUES (?, ?), (?, ?), (?, ?)" 42 "hello" 35 "world" 64 "dollars"]) diff --git a/src/next/jdbc/transaction.clj b/src/next/jdbc/transaction.clj index ca6cfc2..dea3959 100644 --- a/src/next/jdbc/transaction.clj +++ b/src/next/jdbc/transaction.clj @@ -1,7 +1,7 @@ ;; copyright (c) 2018-2019 Sean Corfield, all rights reserved (ns next.jdbc.transaction - "" + "SQL Transaction logic." (:require [next.jdbc.protocols :as p]) (:import (java.sql Connection SQLException))) @@ -17,7 +17,11 @@ :serializable Connection/TRANSACTION_SERIALIZABLE}) (defn- transact* - "" + "Run the given function inside a transaction created on the given connection. + + Isolation level options can be provided, as well as marking the transaction + as read-only and/or rollback-only (so it will automatically rollback + instead of committing any changes)." [^Connection con f opts] (let [{:keys [isolation read-only rollback-only]} opts old-autocommit (.getAutoCommit con)