Add docstrings to everything

This commit is contained in:
Sean Corfield 2019-03-31 23:17:12 -07:00
parent 8646472e79
commit 6e08557d92
9 changed files with 233 additions and 56 deletions

View file

@ -1,3 +0,0 @@
# Introduction to next.jdbc
TODO: write [great documentation](http://jacobian.org/writing/what-to-write/)

View file

@ -1,7 +1,41 @@
;; copyright (c) 2018-2019 Sean Corfield, all rights reserved ;; copyright (c) 2018-2019 Sean Corfield, all rights reserved
(ns next.jdbc (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,
* 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
of hash maps representing the rows; this can be datafied to allow
navigation of foreign keys into other tables (either by convention or
via a schema definition).
* with-transaction -- execute a series of SQL operations within a transaction.
In addition, there are some utility functions that make common operations
easier by providing some syntactic sugar over 'execute!'.
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),
* :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
data; defaults to a function that produces a datafiable hash map.
The following options are supported where 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 (:require [next.jdbc.connection] ; used to extend protocols
[next.jdbc.prepare :as prepare] ; used to extend protocols [next.jdbc.prepare :as prepare] ; used to extend protocols
[next.jdbc.protocols :as p] [next.jdbc.protocols :as p]
@ -11,11 +45,31 @@
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
(defn get-datasource [spec] (p/get-datasource spec)) (defn get-datasource
"Given some sort of specification of a database, return a DataSource."
[spec]
(p/get-datasource spec))
(defn get-connection [spec opts] (p/get-connection spec opts)) (defn get-connection
"Given some sort of specification of a database, return a new Connection.
(defn prepare [spec sql-params opts] (p/prepare spec sql-params opts)) In general, this should be used via with-open:
(with-open [con (get-connection spec opts)]
(run-some-ops con))"
[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.
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))
(defn reducible! (defn reducible!
"General SQL execution function. "General SQL execution function.
@ -26,7 +80,9 @@
(p/-execute connectable sql-params opts))) (p/-execute connectable sql-params opts)))
(defn execute! (defn execute!
"" "General SQL execution function.
Invokes 'reducible!' and then reduces that into a vector of hash maps."
([stmt] ([stmt]
(rs/execute! stmt [] {})) (rs/execute! stmt [] {}))
([connectable sql-params] ([connectable sql-params]
@ -35,7 +91,9 @@
(rs/execute! connectable sql-params opts))) (rs/execute! connectable sql-params opts)))
(defn execute-one! (defn execute-one!
"" "General SQL execution function that returns just the first row of a result.
Invokes 'reducible!' but immediately returns the first row."
([stmt] ([stmt]
(rs/execute-one! stmt [] {})) (rs/execute-one! stmt [] {}))
([connectable sql-params] ([connectable sql-params]
@ -44,11 +102,22 @@
(rs/execute-one! connectable sql-params opts))) (rs/execute-one! connectable sql-params opts)))
(defmacro with-transaction (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
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."
[[sym connectable opts] & body] [[sym connectable opts] & body]
`(p/-transact ~connectable (fn [~sym] ~@body) ~opts)) `(p/-transact ~connectable (fn [~sym] ~@body) ~opts))
(defn insert! (defn insert!
"" "Given a connectable object, a table name, and a data hash map, inserts the
data as a single row in the database and attempts to return a map of generated
keys."
([connectable table key-map] ([connectable table key-map]
(rs/execute! connectable (rs/execute! connectable
(sql/for-insert table key-map {}) (sql/for-insert table key-map {})
@ -59,7 +128,10 @@
(merge {:return-keys true} opts)))) (merge {:return-keys true} opts))))
(defn insert-multi! (defn insert-multi!
"" "Given a connectable object, a table name, a sequence of column names, and
a vector of rows of data (vectors of column values), inserts the data as
multiple rows in the database and attempts to return a vector of maps of
generated keys."
([connectable table cols rows] ([connectable table cols rows]
(rs/execute! connectable (rs/execute! connectable
(sql/for-insert-multi table cols rows {}) (sql/for-insert-multi table cols rows {})
@ -70,14 +142,19 @@
(merge {:return-keys true} opts)))) (merge {:return-keys true} opts))))
(defn find-by-keys (defn find-by-keys
"" "Given a connectable object, a table name, and a hash map of columns and
their values, returns a vector of hash maps of rows that match."
([connectable table key-map] ([connectable table key-map]
(rs/execute! connectable (sql/for-query table key-map {}) {})) (rs/execute! connectable (sql/for-query table key-map {}) {}))
([connectable table key-map opts] ([connectable table key-map opts]
(rs/execute! connectable (sql/for-query table key-map opts) opts))) (rs/execute! connectable (sql/for-query table key-map opts) opts)))
(defn get-by-id (defn get-by-id
"" "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
in the five-argument call."
([connectable table pk] ([connectable table pk]
(rs/execute-one! connectable (sql/for-query table {:id pk} {}) {})) (rs/execute-one! connectable (sql/for-query table {:id pk} {}) {}))
([connectable table pk opts] ([connectable table pk opts]
@ -86,14 +163,18 @@
(rs/execute-one! connectable (sql/for-query table {pk-name pk} opts) opts))) (rs/execute-one! connectable (sql/for-query table {pk-name pk} opts) opts)))
(defn update! (defn update!
"" "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
of a SQL where clause and parameters, perform an update on the table."
([connectable table key-map where-params] ([connectable table key-map where-params]
(rs/execute! connectable (sql/for-update table key-map where-params {}) {})) (rs/execute! connectable (sql/for-update table key-map where-params {}) {}))
([connectable table key-map where-params opts] ([connectable table key-map where-params opts]
(rs/execute! connectable (sql/for-update table key-map where-params opts) opts))) (rs/execute! connectable (sql/for-update table key-map where-params opts) opts)))
(defn delete! (defn delete!
"" "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,
perform a delete on the table."
([connectable table where-params] ([connectable table where-params]
(rs/execute! connectable (sql/for-delete table where-params {}) {})) (rs/execute! connectable (sql/for-delete table where-params {}) {}))
([connectable table where-params opts] ([connectable table where-params opts]

View file

@ -1,7 +1,7 @@
;; copyright (c) 2018-2019 Sean Corfield, all rights reserved ;; copyright (c) 2018-2019 Sean Corfield, all rights reserved
(ns next.jdbc.connection (ns next.jdbc.connection
"" "Standard implementations of get-datasource and get-connection."
(:require [next.jdbc.protocols :as p]) (:require [next.jdbc.protocols :as p])
(:import (java.sql Connection DriverManager) (:import (java.sql Connection DriverManager)
(javax.sql DataSource) (javax.sql DataSource)
@ -81,7 +81,7 @@
(DriverManager/getConnection url (as-properties etc))) (DriverManager/getConnection url (as-properties etc)))
(defn- spec->url+etc (defn- spec->url+etc
"" "Given a database spec, return a JDBC URL and a map of any additional options."
[{:keys [dbtype dbname host port classname] :as db-spec}] [{:keys [dbtype dbname host port classname] :as db-spec}]
(let [;; allow aliases for dbtype (let [;; allow aliases for dbtype
subprotocol (aliases dbtype dbtype) subprotocol (aliases dbtype dbtype)
@ -118,12 +118,13 @@
[url etc])) [url etc]))
(defn- string->url+etc (defn- string->url+etc
"" "Given a JDBC URL, return it with an empty set of options with no parsing."
[s] [s]
[s {}]) [s {}])
(defn- url+etc->datasource (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."
[[url etc]] [[url etc]]
(reify DataSource (reify DataSource
(getConnection [_] (getConnection [_]
@ -136,14 +137,19 @@
(defn- make-connection (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." 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."
^Connection ^Connection
[^DataSource datasource opts] [^DataSource datasource opts]
(let [^Connection connection (.getConnection datasource)] (let [^Connection connection (.getConnection datasource)]
(when (contains? opts :auto-commit?) (when (contains? opts :auto-commit)
(.setAutoCommit connection (boolean (:auto-commit? opts)))) (.setAutoCommit connection (boolean (:auto-commit opts))))
(when (contains? opts :read-only?) (when (contains? opts :read-only)
(.setReadOnly connection (boolean (:read-only? opts)))) (.setReadOnly connection (boolean (:read-only opts))))
connection)) connection))
(extend-protocol p/Sourceable (extend-protocol p/Sourceable

View file

@ -1,7 +1,11 @@
;; copyright (c) 2018-2019 Sean Corfield, all rights reserved ;; copyright (c) 2018-2019 Sean Corfield, all rights reserved
(ns next.jdbc.prepare (ns next.jdbc.prepare
"" "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
that you wish to reuse and (re)set the parameters on it."
(:require [next.jdbc.protocols :as p]) (:require [next.jdbc.protocols :as p])
(:import (java.sql Connection (:import (java.sql Connection
PreparedStatement PreparedStatement
@ -11,7 +15,10 @@
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
(defn set-parameters (defn set-parameters
"" "Given a PreparedStatement and a vector of parameter values, update the
PreparedStatement with those parameters and return it.
Currently uses .setObject with no possibility of an override."
^java.sql.PreparedStatement ^java.sql.PreparedStatement
[^PreparedStatement ps params] [^PreparedStatement ps params]
(when (seq params) (when (seq params)

View file

@ -1,7 +1,31 @@
;; copyright (c) 2018-2019 Sean Corfield, all rights reserved ;; copyright (c) 2018-2019 Sean Corfield, all rights reserved
(ns next.jdbc.protocols (ns next.jdbc.protocols
"") "This is the extensible core of the next generation java.jdbc library.
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).
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.
-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).
prepare -- given SQL and parameters, produce a PreparedStatement that can
be executed (by -execute above); implementation is provided for
Connection.
-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).")
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)

View file

@ -1,7 +1,7 @@
;; copyright (c) 2018-2019 Sean Corfield, all rights reserved ;; copyright (c) 2018-2019 Sean Corfield, all rights reserved
(ns next.jdbc.result-set (ns next.jdbc.result-set
"" "An implementation of ResultSet handling functions."
(:require [clojure.core.protocols :as core-p] (:require [clojure.core.protocols :as core-p]
[next.jdbc.prepare :as prepare] [next.jdbc.prepare :as prepare]
[next.jdbc.protocols :as p]) [next.jdbc.protocols :as p])
@ -12,7 +12,11 @@
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
(defn- get-column-names (defn- get-column-names
"" "Given a ResultSet, return a vector of columns names, each qualified by
the table from which it came.
If :identifiers was specified, apply that to both the table qualifier
and the column name."
[^ResultSet rs opts] [^ResultSet rs opts]
(let [^ResultSetMetaData rsmeta (.getMetaData rs) (let [^ResultSetMetaData rsmeta (.getMetaData rs)
idxs (range 1 (inc (.getColumnCount rsmeta)))] idxs (range 1 (inc (.getColumnCount rsmeta)))]
@ -75,7 +79,13 @@
(range 1 (inc (count @cols))))))))) (range 1 (inc (count @cols)))))))))
(defn- reduce-stmt (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
function and initial value.
If the statement yields neither a ResultSet nor generated keys, return
a hash map containing ::update-count and the number of rows updated,
with the supplied function and initial value applied."
[^PreparedStatement stmt f init opts] [^PreparedStatement stmt f init opts]
(if-let [^ResultSet rs (if (.execute stmt) (if-let [^ResultSet rs (if (.execute stmt)
(.getResultSet stmt) (.getResultSet stmt)
@ -130,19 +140,27 @@
(declare navize-row) (declare navize-row)
(defn datafiable-row (defn datafiable-row
"Given a connectable object, return a function that knows how to turn a row
into a datafiable object that can be 'nav'igated."
[connectable opts] [connectable opts]
(fn [row] (fn [row]
(into (with-meta {} {`core-p/datafy (navize-row connectable opts)}) row))) (into (with-meta {} {`core-p/datafy (navize-row connectable opts)}) row)))
(defn execute! (defn execute!
"" "Given a connectable object and SQL and parameters, execute it and reduce it
into a vector of processed hash maps (rows).
By default, this will create datafiable rows but :row-fn can override that."
[connectable sql-params opts] [connectable sql-params opts]
(into [] (into []
(map (or (:row-fn opts) (datafiable-row connectable opts))) (map (or (:row-fn opts) (datafiable-row connectable opts)))
(p/-execute connectable sql-params opts))) (p/-execute connectable sql-params opts)))
(defn execute-one! (defn execute-one!
"" "Given a connectable object and SQL and parameters, execute it and return
just the first processed hash map (row).
By default, this will create a datafiable row but :row-fn can override that."
[connectable sql-params opts] [connectable sql-params opts]
(let [row-fn (or (:row-fn opts) (datafiable-row connectable opts))] (let [row-fn (or (:row-fn opts) (datafiable-row connectable opts))]
(reduce (fn [_ row] (reduce (fn [_ row]
@ -161,7 +179,22 @@
[(keyword table) :id]))) [(keyword table) :id])))
(defn- navize-row (defn- navize-row
"" "Given a connectable object, return a function that knows how to turn a row
into a navigable object.
A :schema option can provide a map of qualified column names (:table/column)
to tuples that indicate which table they are a foreign key for, 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 <table>id or
<table>_id is used, and the assumption is that such columns are foreign keys
in the <table> portion of their name, the key is called 'id', and the
cardinality is :one.
Rows are looked up using 'execute!' or 'execute-one!' and the :entities
function, if provided, is applied to both the assumed table name and the
assumed foreign key column name."
[connectable opts] [connectable opts]
(fn [row] (fn [row]
(with-meta row (with-meta row

View file

@ -6,11 +6,16 @@
This is intended to provide a minimal level of parity with clojure.java.jdbc This is intended to provide a minimal level of parity with clojure.java.jdbc
(insert!, update!, delete!, etc). For anything more complex, use a library (insert!, update!, delete!, etc). For anything more complex, use a library
like HoneySQL https://github.com/jkk/honeysql to generate SQL + parameters." like HoneySQL https://github.com/jkk/honeysql to generate SQL + parameters.
This is primarily intended to be an implementation detail."
(:require [clojure.string :as str])) (:require [clojure.string :as str]))
(defn by-keys (defn by-keys
"" "Given a hash map of column names and values and a clause type (:set, :where),
return a vector of a SQL clause and its parameters.
Applies any :entities function supplied in the options."
[key-map clause opts] [key-map clause opts]
(let [entity-fn (:entities opts identity) (let [entity-fn (:entities opts identity)
[where params] (reduce-kv (fn [[conds params] k v] [where params] (reduce-kv (fn [[conds params] k v]
@ -25,17 +30,25 @@
params))) params)))
(defn as-keys (defn as-keys
"" "Given a hash map of column names and values, return a string of all the
column names.
Applies any :entities function supplied in the options."
[key-map opts] [key-map opts]
(str/join ", " (map (comp (:entities opts identity) name) (keys key-map)))) (str/join ", " (map (comp (:entities opts identity) name) (keys key-map))))
(defn as-? (defn as-?
"" "Given a hash map of column names and values, or a vector of column names,
return a string of ? placeholders for them."
[key-map opts] [key-map opts]
(str/join ", " (repeat (count key-map) "?"))) (str/join ", " (repeat (count key-map) "?")))
(defn for-query (defn for-query
"" "Given a table name and either a hash map of column names and values or a
vector of SQL (where clause) and its parameters, return a vector of the
full SELECT SQL string and its parameters.
Applies any :entities function supplied in the options."
[table where-params opts] [table where-params opts]
(let [entity-fn (:entities opts identity) (let [entity-fn (:entities opts identity)
where-params (if (map? where-params) where-params (if (map? where-params)
@ -47,7 +60,11 @@
(rest where-params)))) (rest where-params))))
(defn for-delete (defn for-delete
"" "Given a table name and either a hash map of column names and values or a
vector of SQL (where clause) and its parameters, return a vector of the
full DELETE SQL string and its parameters.
Applies any :entities function supplied in the options."
[table where-params opts] [table where-params opts]
(let [entity-fn (:entities opts identity) (let [entity-fn (:entities opts identity)
where-params (if (map? where-params) where-params (if (map? where-params)
@ -59,7 +76,12 @@
(rest where-params)))) (rest where-params))))
(defn for-update (defn for-update
"" "Given a table name, a vector of column names to set and their values, and
either a hash map of column names and values or a vector of SQL (where clause)
and its parameters, return a vector of the full UPDATE SQL string and its
parameters.
Applies any :entities function supplied in the options."
[table key-map where-params opts] [table key-map where-params opts]
(let [entity-fn (:entities opts identity) (let [entity-fn (:entities opts identity)
set-params (by-keys key-map :set opts) set-params (by-keys key-map :set opts)
@ -74,7 +96,10 @@
(into (rest where-params))))) (into (rest where-params)))))
(defn for-insert (defn for-insert
"" "Given a table name and a hash map of column names and their values,
return a vector of the full INSERT SQL string and its parameters.
Applies any :entities function supplied in the options."
[table key-map opts] [table key-map opts]
(let [entity-fn (:entities opts identity) (let [entity-fn (:entities opts identity)
params (as-keys key-map opts) params (as-keys key-map opts)
@ -85,7 +110,11 @@
(vals key-map)))) (vals key-map))))
(defn for-insert-multi (defn for-insert-multi
"" "Given a table name, a vector of column names, and a vector of row values
(each row is a vector of its values), return a vector of the full INSERT
SQL string and its parameters.
Applies any :entities function supplied in the options."
[table cols rows opts] [table cols rows opts]
(assert (apply = (count cols) (map count rows))) (assert (apply = (count cols) (map count rows)))
(let [entity-fn (:entities opts identity) (let [entity-fn (:entities opts identity)

View file

@ -19,19 +19,19 @@
(defn- transact* (defn- transact*
"" ""
[^Connection con f opts] [^Connection con f opts]
(let [{:keys [isolation read-only? rollback-only?]} opts (let [{:keys [isolation read-only rollback-only]} opts
old-autocommit (.getAutoCommit con) old-autocommit (.getAutoCommit con)
old-isolation (.getTransactionIsolation con) old-isolation (.getTransactionIsolation con)
old-readonly (.isReadOnly con)] old-readonly (.isReadOnly con)]
(io! (io!
(when isolation (when isolation
(.setTransactionIsolation con (isolation isolation-levels))) (.setTransactionIsolation con (isolation isolation-levels)))
(when read-only? (when read-only
(.setReadOnly con true)) (.setReadOnly con true))
(.setAutoCommit con false) (.setAutoCommit con false)
(try (try
(let [result (f con)] (let [result (f con)]
(if rollback-only? (if rollback-only
(.rollback con) (.rollback con)
(.commit con)) (.commit con))
result) result)
@ -58,7 +58,7 @@
(try (try
(.setTransactionIsolation con old-isolation) (.setTransactionIsolation con old-isolation)
(catch Exception _))) (catch Exception _)))
(when read-only? (when read-only
(try (try
(.setReadOnly con old-readonly) (.setReadOnly con old-readonly)
(catch Exception _)))))))) (catch Exception _))))))))

View file

@ -1,4 +1,5 @@
(ns next.jdbc-test (ns next.jdbc-test
"Not exactly a test suite -- more a series of examples."
(:require [clojure.test :refer [deftest is testing]] (:require [clojure.test :refer [deftest is testing]]
[next.jdbc :refer :all] [next.jdbc :refer :all]
[next.jdbc.result-set :as rs])) [next.jdbc.result-set :as rs]))
@ -9,17 +10,15 @@
(comment (comment
(def db-spec {:dbtype "h2:mem" :dbname "perf"}) (def db-spec {:dbtype "h2:mem" :dbname "perf"})
(def db-spec {:dbtype "derby" :dbname "perf" :create true}) ;; these should be equivalent
(def db-spec {:dbtype "mysql" :dbname "worldsingles" :user "root" :password "visual"})
(def con db-spec)
(def con (get-datasource db-spec))
(get-connection con {})
(def con (get-connection (get-datasource db-spec) {})) (def con (get-connection (get-datasource db-spec) {}))
(def con (get-connection db-spec {})) (def con (get-connection db-spec {}))
(execute! con ["DROP TABLE fruit"]) (execute! con ["DROP TABLE fruit"])
;; h2 ;; h2
(execute! con ["CREATE TABLE fruit (id int default 0, name varchar(32) primary key, appearance varchar(32), cost int, grade real)"]) (execute! con ["CREATE TABLE fruit (id int default 0, name varchar(32) primary key, appearance varchar(32), cost int, grade real)"])
;; either this...
(execute! con ["INSERT INTO fruit (id,name,appearance,cost,grade) VALUES (1,'Apple','red',59,87), (2,'Banana','yellow',29,92.2), (3,'Peach','fuzzy',139,90.0), (4,'Orange','juicy',89,88.6)"]) (execute! con ["INSERT INTO fruit (id,name,appearance,cost,grade) VALUES (1,'Apple','red',59,87), (2,'Banana','yellow',29,92.2), (3,'Peach','fuzzy',139,90.0), (4,'Orange','juicy',89,88.6)"])
;; ...or this
(insert-multi! con :fruit [:id :name :appearance :cost :grade] (insert-multi! con :fruit [:id :name :appearance :cost :grade]
[[1 "Apple" "red" 59 87] [[1 "Apple" "red" 59 87]
[2,"Banana","yellow",29,92.2] [2,"Banana","yellow",29,92.2]
@ -30,7 +29,7 @@
(execute! con ["CREATE TABLE fruit (id int auto_increment, name varchar(32), appearance varchar(32), cost int, grade real, primary key (id))"]) (execute! con ["CREATE TABLE fruit (id int auto_increment, name varchar(32), appearance varchar(32), cost int, grade real, primary key (id))"])
(execute! con ["INSERT INTO fruit (id,name,appearance,cost,grade) VALUES (1,'Apple','red',59,87), (2,'Banana','yellow',29,92.2), (3,'Peach','fuzzy',139,90.0), (4,'Orange','juicy',89,88.6)"] (execute! con ["INSERT INTO fruit (id,name,appearance,cost,grade) VALUES (1,'Apple','red',59,87), (2,'Banana','yellow',29,92.2), (3,'Peach','fuzzy',139,90.0), (4,'Orange','juicy',89,88.6)"]
{:return-keys true}) {:return-keys true})
;; when you're done
(.close con) (.close con)
(require '[criterium.core :refer [bench quick-bench]]) (require '[criterium.core :refer [bench quick-bench]])
@ -64,7 +63,9 @@
(execute! con ["select * from fruit where appearance = ?" "red"])) (execute! con ["select * from fruit where appearance = ?" "red"]))
(execute! con ["select * from fruit"]) (execute! con ["select * from fruit"])
;; this is not quite equivalent
(into [] (map (partial into {})) (reducible! con ["select * from fruit"])) (into [] (map (partial into {})) (reducible! con ["select * from fruit"]))
;; but this is (equivalent to execute!)
(into [] (map (rs/datafiable-row con {})) (reducible! con ["select * from fruit"])) (into [] (map (rs/datafiable-row con {})) (reducible! con ["select * from fruit"]))
;; with a prepopulated prepared statement ;; with a prepopulated prepared statement
@ -105,11 +106,10 @@
["select * from fruit where appearance = ?" "red"] ["select * from fruit where appearance = ?" "red"]
{:row-fn #(assoc % :test :value)}) {:row-fn #(assoc % :test :value)})
(with-transaction [t con {:rollback-only? true}] (with-transaction [t con {:rollback-only true}]
(execute! t ["INSERT INTO fruit (id,name,appearance,cost,grade) VALUES (5,'Pear','green',49,47)"]) (execute! t ["INSERT INTO fruit (id,name,appearance,cost,grade) VALUES (5,'Pear','green',49,47)"])
(execute! t ["select * from fruit where name = ?" "Pear"])) (execute! t ["select * from fruit where name = ?" "Pear"]))
(execute! con ["select * from fruit where name = ?" "Pear"]) (execute! con ["select * from fruit where name = ?" "Pear"])
(delete! con :fruit {:id 1})
(update! con :fruit {:appearance "Brown"} {:name "Banana"}) (delete! con :fruit {:id 1})
(execute! con ["select * from membership"])) (update! con :fruit {:appearance "Brown"} {:name "Banana"}))