BUG: resultset-seq breaks on duplicate column names

17 views
Skip to first unread message

Allen Rohner

unread,
Oct 11, 2008, 5:21:21 PM10/11/08
to Clojure
If you create a SQL query that returns duplicate names, resultset-seq
throws an exception: "java.lang.RuntimeException:
java.lang.IllegalArgumentException: Too many arguments to struct
constructor"

i.e.

(let [rs (query "select description,description from table")]
(resultset-seq rs)

The following patch adds a method duplicate-col-names? which returns
true if the resultset contains duplicate column names, and modifies
resultset-seq to throw an exception if there are duplicate column
names.

As an aside, now that clojure has real libraries, it seems that
boot.clj is not the best place for the resultset code.

Allen

Index: src/clj/clojure/boot.clj
===================================================================
--- src/clj/clojure/boot.clj (revision 1060)
+++ src/clj/clojure/boot.clj (working copy)
@@ -1901,10 +1901,26 @@
(clojure.lang.LineNumberingPushbackReader.))]
(load-reader rdr)))

+(defn set
+ "Returns a set of the distinct elements of coll."
+ [coll] (apply hash-set coll))
+
+(defn duplicate-col-names? [#^java.sql.ResultSet rs]
+ "returns true if the columns in the result set contain duplicate
names"
+ (let [rsmeta (. rs (getMetaData))
+ idxs (range 1 (inc (. rsmeta (getColumnCount))))
+ keys (map (comp keyword (memfn toLowerCase))
+ (map (fn [i] (. rsmeta (getColumnName i))) idxs))
+ unique-keys (set keys)]
+ (not (= (count keys) (count unique-keys)))))
+
+
(defn resultset-seq
"Creates and returns a lazy sequence of structmaps corresponding to
the rows in the java.sql.ResultSet rs"
[#^java.sql.ResultSet rs]
+ (when (duplicate-col-names? rs)
+ (throw (Exception. "resultset-seq does not handle queries with
duplicate column names")))
(let [rsmeta (. rs (getMetaData))
idxs (range 1 (inc (. rsmeta (getColumnCount))))
keys (map (comp keyword (memfn toLowerCase))
@@ -1916,10 +1932,6 @@
(lazy-cons (apply struct row-struct (row-values)) (thisfn))))]
(rows)))

-(defn set
- "Returns a set of the distinct elements of coll."
- [coll] (apply hash-set coll))
-
(defn #^{:private true}
filter-key [keyfn pred amap]
(loop [ret {} es (seq amap)]

Michael Reid

unread,
Dec 8, 2008, 4:57:50 PM12/8/08
to clo...@googlegroups.com
Has anybody else hit this?

I just did. Its not tricky to alter resultset-seq to return maps with
qualified keys:

(defn resultset-seq
"Creates and returns a lazy sequence of structmaps corresponding to
the rows in the java.sql.ResultSet rs"
[#^java.sql.ResultSet rs]
(let [rsmeta (. rs (getMetaData))
idxs (range 1 (inc (. rsmeta (getColumnCount))))
keys (map (comp keyword (memfn toLowerCase))
- (map (fn [i] (. rsmeta (getColumnName i))) idxs))
+ (map (fn [i] (str (. rsmeta (getTableName i)) "."
(. rsmeta (getColumnName i)))) idxs))
row-struct (apply create-struct keys)
row-values (fn [] (map (fn [#^Integer i] (. rs (getObject i))) idxs))
rows (fn thisfn []
(when (. rs (next))
(lazy-cons (apply struct row-struct (row-values))
(thisfn))))]
(rows)))

Of course now all keys come back as qualified, breaking anything that
relied on the old behaviour. It also wouldn't be too hard to make this
return qualified keys only when there are duplicate column names, as
demonstrated by Allen.

Another thought I had would be to pass a flag to specify if you want
qualified keys which defaults to false--this way you get the old
behaviour unless you ask for/need qualified keys.

Rich, would you consider a patch to make resultset-seq more robust to
handle cases with duplicate column names?

/mike.
Reply all
Reply to author
Forward
0 new messages