com/github/kyleburton/sandbox/sql.clj

Line Coverage Rate: 0.0
     Lines Covered: 0
Branch Coverage Rate: 0.0
    Branches Covered: 0
1(ns com.github.kyleburton.sandbox.sql
2  (use [clojure.contrib.sql :as sql]
3       [com.github.kyleburton.sandbox.utils :as kutils]))
4
5
6(defn- range-sql [end]
7    (range 1 (+ 1 end)))
8
9
10(defn db-schemas
11  "Returns a list of the schema names in the database."
12  [db]
13  (with-connection db
14    (let [schemas (.getSchemas (.getMetaData (connection)))]
15      (loop [has-next (.next schemas)
16             res []]
17        (if has-next
18          (let [schema  (.getString schemas 1)]
19            (recur (.next schemas) 
20                   (conj res schema)))
21          res)))))
22
23(defn schema-tables
24  "Returns a list of maps describing the tables in the database.  The
25  maps include: :catalog, :schema, :name, :type and :remarks as per
26  the JDBC spec."  
27  [db schema]
28  (with-connection db
29    (let [db-meta (.getMetaData (connection))
30          tables (.getTables db-meta nil schema "%" nil)]
31      (loop [has-next (.next tables)
32             res []]
33        (if has-next
34          (let [table {:catalog  (.getString tables  1)
35                       :schema   (.getString tables  2)
36                       :name     (.getString tables  3)
37                       :type     (.getString tables  4)
38                       :remakrs  (.getString tables  5)}]
39            (recur (.next tables)
40                   (conj res table)))
41          res)))))
42
43(defn select-all [table]
44  (format "SELECT * FROM %s" table))
45
46(defn describe-table
47  "Returns a list of column descriptions (maps) for the table.  The
48   maps
49   contain: :name, :catalog, :display-zie, :type, :precision, :scale,
50   :is-auto-increment, :is-case-sensitive, :is-currency,
51   :is-definitely-writable, :is-nullable, :is-read-only,
52   :is-searchable, :is-signed, :is-writable."
53  [db table-name]
54  (with-connection db
55    (let [ps (.prepareStatement (connection) (format "SELECT * FROM %s WHERE 0 = 1" table-name))
56          rs (.executeQuery ps)
57          rs-meta (.getMetaData rs)]
58      (loop [[idx & idxs] (range-sql (.getColumnCount rs-meta))
59             res []]
60        (if idx
61          (recur idxs (conj res {:name                   (.getColumnName rs-meta idx)
62                                 :catalog                (.getCatalogName rs-meta idx)
63                                 :display-size           (.getColumnDisplaySize rs-meta idx)
64                                 :type                   (.getColumnType rs-meta idx)
65                                 :precision              (.getPrecision rs-meta idx)
66                                 :scale                  (.getScale rs-meta idx)
67                                 :is-auto-increment      (.isAutoIncrement rs-meta idx)
68                                 :is-case-sensitive      (.isCaseSensitive rs-meta idx)
69                                 :is-currency            (.isCurrency rs-meta idx)
70                                 :is-definitely-writable (.isDefinitelyWritable rs-meta idx)
71                                 :is-nullable            (.isNullable rs-meta idx)
72                                 :is-read-only           (.isReadOnly rs-meta idx)
73                                 :is-searchable          (.isSearchable rs-meta idx)
74                                 :is-signed              (.isSigned rs-meta idx)
75                                 :is-writable            (.isWritable rs-meta idx)}))
76          res)))))
77
78
79(defn rs->record [rs]
80  (loop [[idx & idxs] (range-sql (.getColumnCount (.getMetaData rs)))
81         res []]
82    (if idx
83      (recur idxs
84             (conj res (.getString rs idx)))
85      res)))
86
87(defn rs->map [rs]
88  (loop [meta (.getMetaData rs)
89         [idx & idxs] (range-sql (.getColumnCount meta))
90         res {}]
91    (if idx
92      (recur meta
93             idxs
94             (assoc res (.getColumnName meta idx) (.getString rs idx)))
95      res)))
96
97;; TODO: support bind variables...
98(defn sql->records [db sql]
99  (with-connection db
100    (let [ps (.prepareStatement (connection) sql)
101          rs (.executeQuery ps)]
102      (loop [has-next (.next rs)
103             res []]
104        (if has-next
105          (let [rec (rs->record rs)]
106            (recur (.next rs) 
107                   (conj res rec)))
108          res)))))
109
110(defn sql->maps [db sql]
111  (with-connection db
112    (let [ps (.prepareStatement (connection) sql)
113          rs (.executeQuery ps)]
114      (loop [has-next (.next rs)
115             res []]
116        (if has-next
117          (let [rec (rs->map rs)]
118            (recur (.next rs) 
119                   (conj res rec)))
120          res)))))
121
122(defmacro do-rs-rows
123  [db sql var & body]
124  `(with-connection ~db
125     (let [ps# (.prepareStatement (connection) ~sql)
126           rs# (.executeQuery ps#)
127           fn# (fn [~var] ~@body)]
128       (loop [has-next# (.next rs#)]
129         (if has-next#
130           (do
131             (fn# (rs->record rs#))
132             (recur (.next rs#))))))))
133
134(defmacro do-rs-maps [db sql var & body]
135  `(with-connection ~db
136     (let [ps# (.prepareStatement (connection) ~sql)
137           rs# (.executeQuery ps#)
138           fn# (fn [~var] ~@body)]
139       (loop [has-next# (.next rs#)]
140         (if has-next#
141           (do
142             (fn# (rs->map rs#))
143             (recur (.next rs#))))))))
144