I have a function to get JDBC metadata through clojure-sql
(ns relink
(:require
[clojure.contrib.sql :as sql]
[clojure.string :as str]
))
(let [db-host "localhost"
db-port 1433
db-name "databasename"]
(def db {:classname "com.microsoft.sqlserver.jdbc.SQLServerDriver"
:subprotocol "sqlserver"
:subname (str "//" db-host ":" db-port)
:databasename db-name
:user "user"
:password "password"}))
(defn get-table-metadata
"Take database spec, return all table names from the database metadata"
[db]
(sql/with-connection db
(doall
(resultset-seq
(.getTables
(.getMetaData (sql/connection))
nil nil "%" (into-array '("TABLE")))))))
(get-table-metadata db)
Now I want to extend that function to use other SQL metadata, by wrapping the java method call in a clojure function as parameter, to look something like:
(get-sql-metadata db .getTables nil nil "%" (into-array '("TABLE")))
There’s seems no way to get this done without putting (.getMetaData (sql/connection)) inside the function parameter as well.
(get-sql-metadata db #(.getTables (.getMetaData (sql/connection)) nil nil "%" (into-array '("TABLE"))))
However, I would like to abstract this away inside the get-sql-metadata function, since it’s the same for all metadata method calls.
I’ve tried rewrite the part within resultset-seq with doto, .., -> and (. notations but could get none of them to work.
What did I miss ?
UPDATE:
The following solution works, but there’s some hacking needed. I can’t believe the reflection in str-invoke should be needed, so I won’t post it as an answer.
(defn str-invoke [instance method-str & args]
(clojure.lang.Reflector/invokeInstanceMethod
instance
method-str
(to-array args)))
(defn get-sql-metadata
"Take database spec, metadata method (as string) and method parameters"
[db method & args]
(sql/with-connection db
(doall
(resultset-seq
(apply str-invoke
(.getMetaData (sql/connection))
method args)))))
(get-sql-metadata db "getTables" nil nil "%" (into-array '("TABLE")))
The problem seems to be that apply is needed,but can’t be used on the . form.
As making get-sql-metadata a function will cause clojure to evaluate arguments before applying the function it is impossible to pass it an “unknown” symbol like .getTables which can’t be evaluated to the function.
In macros on the otherhand evaluate arguments aren’t evaluated before aplying the macro.
So making the function a Macro would allow you to do this.
like:
And of course there is the possibility of transforming the method call into a clojure function by using the macro memfn, but i don’t think that’s what you want