On one of the Common Lisp projects I worked on (a very typical Web app), I used the CLSQL library to integrate with a Postgres database. CLSQL is itself an opensource version of the SQL package which is part of Lispworks. One of the more interesting features of this library was an s-expression based query language, which emitted SQL query strings behind the scenes. So, for example:
(select [count 1] :from '([person]) :where [and [= [role] +role-programmer+] [< [age] 30]))
would emit the following SQL (and actually runs the query):
SELECT COUNT(1) FROM person WHERE (role = 1 AND age < 30)
Nice. Just being able to write the WHERE clause as a readable s-expression makes CLSQL worthwhile. Of course, it does much more than just that.
Anyway, Clojure has a library in clojure-contrib, for integrating with SQL databases using JDBC. But, as you can see from the examples on that wiki page, SELECT, INSERT and UPDATE more or less require hand built SQL query strings. So, I figured I'd try and emulate the CLSQL approach using Clojure macrology. Here's what I was able to do:
(sql-select-stmt "count(1)" :from '(person) :where (sql-and (sql-= 'role +role-programmer+) (sql-< 'age 30)))
Not as concise as CLSQL (the "sql-" prefix interrupts the flow), but
perhaps the best we can do, considering Clojure doesn't have
programmable reader macros (at least not without serious
hackery). CLSQL cleverly uses reader macros to do interesting things
with expressions which are inside square brackets, like turning [or
...]
into (sql-or ...)
, and [a-table-name]
into "atablename"
etc.
Incidentally, sql-select-stmt
doesn't run the query, just generates
the query string, which can be passed to with-results
, like so:
(with-results rs (sql-select-stmt "count(1)" :from from :where where) (:count (first rs)))
The code for the sql macros is a bit long to insert here, so I'm thinking I'll create a cynojure repo on github, and put all the various bits of clojure code I create in there. I'll post about that separately.
Peace.