view src/clojure/contrib/sql.clj @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
line wrap: on
line source
1 ;; Copyright (c) Stephen C. Gilardi. All rights reserved. The use and
2 ;; distribution terms for this software are covered by the Eclipse Public
3 ;; License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can
4 ;; be found in the file epl-v10.html at the root of this distribution. By
5 ;; using this software in any fashion, you are agreeing to be bound by the
6 ;; terms of this license. You must not remove this notice, or any other,
7 ;; from this software.
8 ;;
9 ;; sql.clj
10 ;;
11 ;; A Clojure interface to sql databases via jdbc
12 ;;
13 ;; See clojure.contrib.sql.test for an example
14 ;;
15 ;; scgilardi (gmail)
16 ;; Created 2 April 2008
18 (ns
19 ^{:author "Stephen C. Gilardi",
20 :doc "A Clojure interface to sql databases via jdbc."
21 :see-also [["http://github.com/richhickey/clojure-contrib/blob/master/src/test/clojure/clojure/contrib/test_sql.clj"
22 "Example code"]]}
23 clojure.contrib.sql
24 (:use (clojure.contrib
25 [def :only (defalias)]
26 [string :only (as-str)])
27 clojure.contrib.sql.internal))
29 (defalias find-connection find-connection*)
30 (defalias connection connection*)
32 (defmacro with-connection
33 "Evaluates body in the context of a new connection to a database then
34 closes the connection. db-spec is a map containing values for one of the
35 following parameter sets:
37 Factory:
38 :factory (required) a function of one argument, a map of params
39 (others) (optional) passed to the factory function in a map
41 DriverManager:
42 :classname (required) a String, the jdbc driver class name
43 :subprotocol (required) a String, the jdbc subprotocol
44 :subname (required) a String, the jdbc subname
45 (others) (optional) passed to the driver as properties.
47 DataSource:
48 :datasource (required) a javax.sql.DataSource
49 :username (optional) a String
50 :password (optional) a String, required if :username is supplied
52 JNDI:
53 :name (required) a String or javax.naming.Name
54 :environment (optional) a java.util.Map"
55 [db-spec & body]
56 `(with-connection* ~db-spec (fn [] ~@body)))
58 (defmacro transaction
59 "Evaluates body as a transaction on the open database connection. Any
60 nested transactions are absorbed into the outermost transaction. By
61 default, all database updates are committed together as a group after
62 evaluating the outermost body, or rolled back on any uncaught
63 exception. If set-rollback-only is called within scope of the outermost
64 transaction, the entire transaction will be rolled back rather than
65 committed when complete."
66 [& body]
67 `(transaction* (fn [] ~@body)))
69 (defn set-rollback-only
70 "Marks the outermost transaction such that it will rollback rather than
71 commit when complete"
72 []
73 (rollback true))
75 (defn is-rollback-only
76 "Returns true if the outermost transaction will rollback rather than
77 commit when complete"
78 []
79 (rollback))
81 (defn do-commands
82 "Executes SQL commands on the open database connection."
83 [& commands]
84 (with-open [stmt (.createStatement (connection))]
85 (doseq [cmd commands]
86 (.addBatch stmt cmd))
87 (transaction
88 (seq (.executeBatch stmt)))))
90 (defn do-prepared
91 "Executes an (optionally parameterized) SQL prepared statement on the
92 open database connection. Each param-group is a seq of values for all of
93 the parameters."
94 [sql & param-groups]
95 (with-open [stmt (.prepareStatement (connection) sql)]
96 (doseq [param-group param-groups]
97 (doseq [[index value] (map vector (iterate inc 1) param-group)]
98 (.setObject stmt index value))
99 (.addBatch stmt))
100 (transaction
101 (seq (.executeBatch stmt)))))
103 (defn create-table
104 "Creates a table on the open database connection given a table name and
105 specs. Each spec is either a column spec: a vector containing a column
106 name and optionally a type and other constraints, or a table-level
107 constraint: a vector containing words that express the constraint. All
108 words used to describe the table may be supplied as strings or keywords."
109 [name & specs]
110 (do-commands
111 (format "CREATE TABLE %s (%s)"
112 (as-str name)
113 (apply str
114 (map as-str
115 (apply concat
116 (interpose [", "]
117 (map (partial interpose " ") specs))))))))
119 (defn drop-table
120 "Drops a table on the open database connection given its name, a string
121 or keyword"
122 [name]
123 (do-commands
124 (format "DROP TABLE %s" (as-str name))))
126 (defn insert-values
127 "Inserts rows into a table with values for specified columns only.
128 column-names is a vector of strings or keywords identifying columns. Each
129 value-group is a vector containing a values for each column in
130 order. When inserting complete rows (all columns), consider using
131 insert-rows instead."
132 [table column-names & value-groups]
133 (let [column-strs (map as-str column-names)
134 n (count (first value-groups))
135 template (apply str (interpose "," (replicate n "?")))
136 columns (if (seq column-names)
137 (format "(%s)" (apply str (interpose "," column-strs)))
138 "")]
139 (apply do-prepared
140 (format "INSERT INTO %s %s VALUES (%s)"
141 (as-str table) columns template)
142 value-groups)))
144 (defn insert-rows
145 "Inserts complete rows into a table. Each row is a vector of values for
146 each of the table's columns in order."
147 [table & rows]
148 (apply insert-values table nil rows))
150 (defn insert-records
151 "Inserts records into a table. records are maps from strings or
152 keywords (identifying columns) to values."
153 [table & records]
154 (doseq [record records]
155 (insert-values table (keys record) (vals record))))
157 (defn delete-rows
158 "Deletes rows from a table. where-params is a vector containing a string
159 providing the (optionally parameterized) selection criteria followed by
160 values for any parameters."
161 [table where-params]
162 (let [[where & params] where-params]
163 (do-prepared
164 (format "DELETE FROM %s WHERE %s"
165 (as-str table) where)
166 params)))
168 (defn update-values
169 "Updates values on selected rows in a table. where-params is a vector
170 containing a string providing the (optionally parameterized) selection
171 criteria followed by values for any parameters. record is a map from
172 strings or keywords (identifying columns) to updated values."
173 [table where-params record]
174 (let [[where & params] where-params
175 column-strs (map as-str (keys record))
176 columns (apply str (concat (interpose "=?, " column-strs) "=?"))]
177 (do-prepared
178 (format "UPDATE %s SET %s WHERE %s"
179 (as-str table) columns where)
180 (concat (vals record) params))))
182 (defn update-or-insert-values
183 "Updates values on selected rows in a table, or inserts a new row when no
184 existing row matches the selection criteria. where-params is a vector
185 containing a string providing the (optionally parameterized) selection
186 criteria followed by values for any parameters. record is a map from
187 strings or keywords (identifying columns) to updated values."
188 [table where-params record]
189 (transaction
190 (let [result (update-values table where-params record)]
191 (if (zero? (first result))
192 (insert-values table (keys record) (vals record))
193 result))))
195 (defmacro with-query-results
196 "Executes a query, then evaluates body with results bound to a seq of the
197 results. sql-params is a vector containing a string providing
198 the (optionally parameterized) SQL query followed by values for any
199 parameters."
200 [results sql-params & body]
201 `(with-query-results* ~sql-params (fn [~results] ~@body)))