2012-07-13 01:50:13 +00:00
# Honey SQL
2013-08-06 20:36:56 +00:00
SQL as Clojure data structures. Build queries programmatically -- even at runtime -- without having to bash strings together.
2012-12-03 17:38:48 +00:00
2015-02-24 06:00:47 +00:00
## Build
[](https://travis-ci.org/jkk/honeysql)
2017-12-18 08:35:48 +00:00
[](https://versions.deps.co/jkk/honeysql)
2019-02-11 22:36:53 +00:00
[](https://clojurians.zulipchat.com/#narrow/stream/152091-honeysql)
2015-02-24 06:00:47 +00:00
2015-03-03 22:39:54 +00:00
## Leiningen Coordinates
2012-08-24 22:49:52 +00:00
2015-03-03 22:39:54 +00:00
[](http://clojars.org/honeysql)
2012-08-24 22:49:52 +00:00
2017-07-19 05:11:49 +00:00
## Note on code samples
All sample code in this README is automatically run as a unit test using
[midje-readme ](https://github.com/boxed/midje-readme ).
Note that while some of these samples show pretty-printed SQL, this is just for
README readability; honeysql does not generate pretty-printed SQL.
The #sql/regularize directive tells the test-runner to ignore the extraneous
whitespace.
2012-07-13 01:50:13 +00:00
## Usage
2017-07-19 05:11:49 +00:00
```clojure
2012-08-24 22:20:58 +00:00
(require '[honeysql.core :as sql]
2017-07-19 05:11:49 +00:00
'[honeysql.helpers :refer :all :as helpers])
2012-07-13 15:46:50 +00:00
```
Everything is built on top of maps representing SQL queries:
2012-07-13 13:57:47 +00:00
2017-07-19 05:11:49 +00:00
```clojure
2012-07-13 13:57:47 +00:00
(def sqlmap {:select [:a :b :c]
:from [:foo]
:where [:= :f.a "baz"]})
2012-07-13 15:46:50 +00:00
```
2019-09-07 20:14:36 +00:00
Column names can be provided as keywords or symbols (but not strings -- HoneySQL treats strings as values that should be lifted out of the SQL as parameters).
2012-07-13 17:13:37 +00:00
`format` turns maps into `clojure.java.jdbc` -compatible, parameterized SQL:
2012-07-13 13:57:47 +00:00
2017-07-19 05:11:49 +00:00
```clojure
2012-07-13 14:53:19 +00:00
(sql/format sqlmap)
2017-07-19 01:01:00 +00:00
=> ["SELECT a, b, c FROM foo WHERE f.a = ?" "baz"]
2012-07-13 15:46:50 +00:00
```
2012-07-13 13:57:47 +00:00
2017-05-21 04:45:20 +00:00
Honeysql is a relatively "pure" library, it does not manage your sql connection
or run queries for you, it simply generates SQL strings. You can then pass them
to jdbc:
```clj
2017-08-21 05:44:06 +00:00
(jdbc/query conn (sql/format sqlmap))
2017-05-21 04:45:20 +00:00
```
2012-08-24 22:31:49 +00:00
You can build up SQL maps yourself or use helper functions. `build` is the Swiss Army Knife helper. It lets you leave out brackets here and there:
2012-08-24 22:20:58 +00:00
2017-07-19 05:11:49 +00:00
```clojure
2012-08-24 22:20:58 +00:00
(sql/build :select :*
:from :foo
:where [:= :f.a "baz"])
=> {:where [:= :f.a "baz"], :from [:foo], :select [:*]}
```
You can provide a "base" map as the first argument to build:
2017-07-19 05:11:49 +00:00
```clojure
2012-08-24 22:20:58 +00:00
(sql/build sqlmap :offset 10 :limit 10)
2017-07-19 05:11:49 +00:00
=> {:limit 10
:offset 10
:select [:a :b :c]
:where [:= :f.a "baz"]
:from [:foo]}
2012-08-24 22:20:58 +00:00
```
There are also functions for each clause type in the `honeysql.helpers` namespace:
2012-07-13 13:57:47 +00:00
2017-07-19 05:11:49 +00:00
```clojure
2012-07-13 15:46:50 +00:00
(-> (select :a :b :c)
(from :foo)
(where [:= :f.a "baz"]))
```
2012-07-13 17:13:37 +00:00
Order doesn't matter:
2012-07-13 15:46:50 +00:00
2017-07-19 05:11:49 +00:00
```clojure
2012-07-13 15:46:50 +00:00
(= (-> (select :*) (from :foo))
(-> (from :foo) (select :*)))
2012-07-13 14:53:19 +00:00
=> true
2012-07-13 15:46:50 +00:00
```
2012-07-13 14:53:19 +00:00
2012-07-13 17:13:37 +00:00
When using the vanilla helper functions, new clauses will replace old clauses:
2012-07-13 13:57:47 +00:00
2017-07-19 05:11:49 +00:00
```clojure
2012-07-13 15:46:50 +00:00
(-> sqlmap (select :*))
2017-07-19 05:11:49 +00:00
=> '{:from [:foo], :where [:= :f.a "baz"], :select (:*)}
2012-07-13 15:46:50 +00:00
```
2012-07-13 17:13:37 +00:00
To add to clauses instead of replacing them, use `merge-select` , `merge-where` , etc.:
2012-07-13 15:46:50 +00:00
2017-07-19 05:11:49 +00:00
```clojure
2012-08-24 22:20:58 +00:00
(-> sqlmap
(merge-select :d :e)
(merge-where [:> :b 10])
sql/format)
2017-07-19 01:01:00 +00:00
=> ["SELECT a, b, c, d, e FROM foo WHERE (f.a = ? AND b > ?)" "baz" 10]
2012-07-13 15:46:50 +00:00
```
2012-07-13 13:57:47 +00:00
2015-03-25 00:31:07 +00:00
`where` will combine multiple clauses together using and:
2017-07-19 05:11:49 +00:00
```clojure
2015-03-25 00:31:07 +00:00
(-> (select :*)
(from :foo)
(where [:= :a 1] [:< :b 100 ] )
sql/format)
2017-07-19 01:01:00 +00:00
=> ["SELECT * FROM foo WHERE (a = ? AND b < ?)" 1 100]
2015-03-25 00:31:07 +00:00
```
2018-06-25 20:00:42 +00:00
Column and table names may be aliased by using a vector pair of the original
name and the desired alias:
```clojure
(-> (select :a [:b :bar] :c [:d :x])
(from [:foo :quux])
(where [:= :quux.a 1] [:< :bar 100 ] )
sql/format)
=> ["SELECT a, b AS bar, c, d AS x FROM foo quux WHERE (quux.a = ? AND bar < ?)" 1 100]
```
In particular, note that `(select [:a :b])` means `SELECT a AS b` rather than
`SELECT a, b` -- `select` is variadic and does not take a collection of column names.
2018-06-25 19:30:48 +00:00
Inserts are supported in two patterns.
2014-10-07 22:07:39 +00:00
In the first pattern, you must explicitly specify the columns to insert,
then provide a collection of rows, each a collection of column values:
2014-06-21 12:23:02 +00:00
2017-07-19 05:11:49 +00:00
```clojure
2014-06-21 12:23:02 +00:00
(-> (insert-into :properties)
2014-06-21 14:14:46 +00:00
(columns :name :surname :age)
2014-06-21 12:23:02 +00:00
(values
2014-06-21 14:14:46 +00:00
[["Jon" "Smith" 34]
["Andrew" "Cooper" 12]
["Jane" "Daniels" 56]])
2014-06-21 12:23:02 +00:00
sql/format)
2017-07-19 05:11:49 +00:00
=> [#sql/regularize
"INSERT INTO properties (name, surname, age)
2017-07-19 01:01:00 +00:00
VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?)"
"Jon" "Smith" 34 "Andrew" "Cooper" 12 "Jane" "Daniels" 56]
2014-06-21 12:23:02 +00:00
```
2014-10-07 22:07:39 +00:00
Alternately, you can simply specify the values as maps; the first map defines the columns to insert,
and the remaining maps *must* have the same set of keys and values:
2017-07-19 05:11:49 +00:00
```clojure
2014-10-07 22:07:39 +00:00
(-> (insert-into :properties)
(values [{:name "John" :surname "Smith" :age 34}
{:name "Andrew" :surname "Cooper" :age 12}
2015-03-17 16:22:05 +00:00
{:name "Jane" :surname "Daniels" :age 56}])
2014-10-07 22:07:39 +00:00
sql/format)
2017-07-19 05:11:49 +00:00
=> [#sql/regularize
"INSERT INTO properties (name, surname, age)
2017-07-19 01:01:00 +00:00
VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?)"
"John" "Smith" 34
"Andrew" "Cooper" 12
"Jane" "Daniels" 56]
2014-10-07 22:07:39 +00:00
```
The column values do not have to be literals, they can be nested queries:
2017-07-19 05:11:49 +00:00
```clojure
2014-10-07 22:07:39 +00:00
(let [user-id 12345
2014-10-22 17:01:46 +00:00
role-name "user"]
2014-10-07 22:07:39 +00:00
(-> (insert-into :user_profile_to_role)
(values [{:user_profile_id user-id
:role_id (-> (select :id)
(from :role)
2014-10-22 17:01:46 +00:00
(where [:= :name role-name]))}])
2014-10-07 22:08:57 +00:00
sql/format))
2014-10-22 17:01:46 +00:00
2017-07-19 05:11:49 +00:00
=> [#sql/regularize
"INSERT INTO user_profile_to_role (user_profile_id, role_id)
2017-07-19 01:01:00 +00:00
VALUES (?, (SELECT id FROM role WHERE name = ?))"
12345
2014-10-22 17:01:46 +00:00
"user"]
2014-10-07 22:07:39 +00:00
```
2019-09-07 22:56:06 +00:00
Composite types are supported:
```clojure
(-> (insert-into :comp_table)
(columns :name :comp_column)
(values
[["small" (composite 1 "inch")]
["large" (composite 10 "feet")]])
sql/format)
=> [#sql/regularize
"INSERT INTO comp_table (name, comp_column)
VALUES (?, (?, ?)), (?, (?, ?))"
"small" 1 "inch" "large" 10 "feet"]
```
2014-06-21 12:23:02 +00:00
Updates are possible too (note the double S in `sset` to avoid clashing
with `clojure.core/set` ):
2017-07-19 05:11:49 +00:00
```clojure
(-> (helpers/update :films)
2014-06-21 14:14:46 +00:00
(sset {:kind "dramatic"
2018-09-27 20:00:58 +00:00
:watched (sql/call :+ :watched 1)})
2014-06-21 14:14:46 +00:00
(where [:= :kind "drama"])
sql/format)
2018-09-27 20:00:58 +00:00
=> [#sql/regularize
"UPDATE films SET kind = ?, watched = (watched + ?)
WHERE kind = ?"
"dramatic"
1
"drama"]
2014-06-21 12:23:02 +00:00
```
2019-09-07 21:55:06 +00:00
If you are trying to build a compound update statement (with `from` or `join` ),
be aware that different databases have slightly different syntax in terms of
where `SET` should appear. The default above is to put `SET` after any `JOIN` .
There are two variants of `sset` (and the underlying `:set` in the SQL map):
* `set0` (and `:set0` ) -- this puts the `SET` before `FROM` ,
* `set1` (and `:set1` ) -- a synonym for `sset` (and `:set` ) that puts the `SET` after `JOIN` .
2014-06-21 12:23:02 +00:00
Deletes look as you would expect:
2017-07-19 05:11:49 +00:00
```clojure
2014-06-21 14:14:46 +00:00
(-> (delete-from :films)
(where [:< > :kind "musical"])
sql/format)
2014-06-21 12:23:02 +00:00
=> ["DELETE FROM films WHERE kind < > ?" "musical"]
```
2018-06-27 01:24:01 +00:00
If your database supports it, you can also delete from multiple tables:
```clojure
(-> (delete [:films :directors])
(from :films)
(join :directors [:= :films.director_id :directors.id])
(where [:< > :kind "musical"])
sql/format)
=> [#sql/regularize
"DELETE films, directors
FROM films
INNER JOIN directors ON films.director_id = directors.id
WHERE kind < > ?"
"musical"]
```
2019-09-07 20:24:46 +00:00
If you want to delete everything from a table, you can use `truncate` :
```clojure
(-> (truncate :films)
sql/format)
=> ["TRUNCATE films"]
```
2012-07-13 15:46:50 +00:00
Queries can be nested:
2017-07-19 05:11:49 +00:00
```clojure
2012-08-24 22:20:58 +00:00
(-> (select :*)
(from :foo)
(where [:in :foo.a (-> (select :a) (from :bar))])
sql/format)
2017-07-19 01:01:00 +00:00
=> ["SELECT * FROM foo WHERE (foo.a in (SELECT a FROM bar))"]
2012-07-13 15:46:50 +00:00
```
2012-07-13 13:57:47 +00:00
2015-08-25 15:25:05 +00:00
Queries may be united within a :union or :union-all keyword:
2017-07-19 05:11:49 +00:00
```clojure
2015-08-25 15:25:05 +00:00
(sql/format {:union [(-> (select :*) (from :foo))
(-> (select :*) (from :bar))]})
2016-11-06 20:29:37 +00:00
=> ["SELECT * FROM foo UNION SELECT * FROM bar"]
2015-08-25 15:25:05 +00:00
```
2013-08-06 18:05:17 +00:00
Keywords that begin with `%` are interpreted as SQL function calls:
2012-07-13 15:46:50 +00:00
2017-07-19 05:11:49 +00:00
```clojure
2013-08-06 18:05:17 +00:00
(-> (select :%count.*) (from :foo) sql/format)
2014-07-25 05:52:08 +00:00
=> ["SELECT count(*) FROM foo"]
(-> (select :%max.id) (from :foo) sql/format)
=> ["SELECT max(id) FROM foo"]
2013-08-06 18:05:17 +00:00
```
2013-08-06 18:27:56 +00:00
Keywords that begin with `?` are interpreted as bindable parameters:
2017-07-19 05:11:49 +00:00
```clojure
2013-08-06 18:27:56 +00:00
(-> (select :id)
(from :foo)
(where [:= :a :?baz])
2013-08-06 19:08:09 +00:00
(sql/format :params {:baz "BAZ"}))
2013-08-06 18:27:56 +00:00
=> ["SELECT id FROM foo WHERE a = ?" "BAZ"]
```
2018-06-30 04:59:17 +00:00
There are helper functions and data literals for SQL function calls, field
qualifiers, raw SQL fragments, inline values, and named input parameters:
2013-08-06 18:05:17 +00:00
2017-07-19 05:11:49 +00:00
```clojure
(def call-qualify-map
(-> (select (sql/call :foo :bar) (sql/qualify :foo :a) (sql/raw "@var := foo.bar"))
(from :foo)
2018-06-25 19:30:48 +00:00
(where [:= :a (sql/param :baz)] [:= :b (sql/inline 42)])))
2012-07-13 13:57:47 +00:00
2017-07-19 05:11:49 +00:00
call-qualify-map
2018-06-25 19:30:48 +00:00
=> '{:where [:and [:= :a #sql/param :baz] [:= :b #sql/inline 42]]
2017-07-19 05:11:49 +00:00
:from (:foo)
:select (#sql/call [:foo :bar] :foo.a #sql/raw "@var := foo.bar")}
(sql/format call-qualify-map :params {:baz "BAZ"})
2018-06-25 19:30:48 +00:00
=> ["SELECT foo(bar), foo.a, @var := foo.bar FROM foo WHERE (a = ? AND b = 42)" "BAZ"]
2012-07-13 13:57:47 +00:00
```
2012-07-13 01:50:13 +00:00
2018-06-30 04:59:17 +00:00
Raw SQL fragments that are strings are treated exactly as-is when rendered into
the formatted SQL string (with no parsing or parameterization). Inline values
will not be lifted out as parameters, so they end up in the SQL string as-is.
Raw SQL can also be supplied as a vector of strings and values. Strings are
rendered as-is into the formatted SQL string. Non-strings are lifted as
parameters. If you need a string parameter lifted, you must use `#sql/param`
or the `param` helper.
```clojure
(-> (select :*)
(from :foo)
(where [:< :expired_at ( sql / raw [ " now ( ) - ' " 5 " seconds ' " ] ) ] )
(sql/format {:foo 5}))
=> ["SELECT * FROM foo WHERE expired_at < now ( ) - ' ? seconds ' " 5 ]
```
```clojure
(-> (select :*)
(from :foo)
(where [:< :expired_at ( sql / raw [" now () - '" #sql/param :t " seconds '"])])
(sql/format {:t 5}))
=> ["SELECT * FROM foo WHERE expired_at < now ( ) - ' ? seconds ' " 5 ]
```
2018-06-25 19:30:48 +00:00
2013-08-06 20:17:05 +00:00
To quote identifiers, pass the `:quoting` keyword option to `format` . Valid options are `:ansi` (PostgreSQL), `:mysql` , or `:sqlserver` :
2013-08-06 19:08:09 +00:00
2017-07-19 05:11:49 +00:00
```clojure
2013-08-06 19:08:09 +00:00
(-> (select :foo.a)
(from :foo)
(where [:= :foo.a "baz"])
(sql/format :quoting :mysql))
=> ["SELECT `foo` .`a` FROM `foo` WHERE `foo` .`a` = ?" "baz"]
```
2019-09-07 22:18:10 +00:00
To issue a locking select, add a `:lock` to the query or use the lock helper. The lock value must be a map with a `:mode` value. The built-in
modes are the standard :update (FOR UPDATE) or the vendor-specific `:mysql-share` (LOCK IN SHARE MODE) or `:postresql-share` (FOR SHARE). The
2015-04-20 01:57:44 +00:00
lock map may also provide a :wait value, which if false will append the NOWAIT parameter, supported by PostgreSQL.
2017-07-19 05:11:49 +00:00
```clojure
2015-04-20 01:57:44 +00:00
(-> (select :foo.a)
(from :foo)
2017-07-19 01:01:00 +00:00
(where [:= :foo.a "baz"])
2016-05-07 18:09:09 +00:00
(lock :mode :update)
2015-04-20 01:57:44 +00:00
(sql/format))
=> ["SELECT foo.a FROM foo WHERE foo.a = ? FOR UPDATE" "baz"]
```
To support novel lock modes, implement the `format-lock-clause` multimethod.
2015-10-16 19:22:04 +00:00
To be able to use dashes in quoted names, you can pass ```:allow-dashed-names true``` as an argument to the ```format``` function.
2017-07-19 05:11:49 +00:00
```clojure
(sql/format
2015-10-16 19:19:42 +00:00
{:select [:f.foo-id :f.foo-name]
:from [[:foo-bar :f]]
:where [:= :f.foo-id 12345]}
:allow-dashed-names? true
:quoting :ansi)
2017-07-19 05:11:49 +00:00
=> ["SELECT \"f\".\"foo-id\", \"f\".\"foo-name\" FROM \"foo-bar\" \"f\" WHERE \"f\".\"foo-id\" = ?" 12345]
2015-10-16 19:19:42 +00:00
```
2012-08-25 03:06:24 +00:00
Here's a big, complicated query. Note that Honey SQL makes no attempt to verify that your queries make any sense. It merely renders surface syntax.
2012-07-13 15:46:50 +00:00
2017-07-19 05:11:49 +00:00
```clojure
(def big-complicated-map
(-> (select :f.* :b.baz :c.quux [:b.bla "bla-bla"]
(sql/call :now) (sql/raw "@x := 10"))
(modifiers :distinct)
(from [:foo :f] [:baz :b])
(join :draq [:= :f.b :draq.x])
(left-join [:clod :c] [:= :f.a :c.d])
(right-join :bock [:= :bock.z :c.e])
(where [:or
[:and [:= :f.a "bort"] [:not= :b.baz (sql/param :param1)]]
[:< 1 2 3 ]
[:in :f.e [1 (sql/param :param2) 3]]
[:between :f.e 10 20]])
(group :f.a)
(having [:< 0 :f . e ] )
(order-by [:b.baz :desc] :c.quux [:f.a :nulls-first])
(limit 50)
(offset 10)))
big-complicated-map
2012-10-19 16:41:26 +00:00
=> {:select [:f.* :b.baz :c.quux [:b.bla "bla-bla"]
(sql/call :now) (sql/raw "@x := 10")]
:modifiers [:distinct]
:from [[:foo :f] [:baz :b]]
:join [:draq [:= :f.b :draq.x]]
:left-join [[:clod :c] [:= :f.a :c.d]]
:right-join [:bock [:= :bock.z :c.e]]
:where [:or
[:and [:= :f.a "bort"] [:not= :b.baz (sql/param :param1)]]
[:< 1 2 3 ]
[:in :f.e [1 (sql/param :param2) 3]]
[:between :f.e 10 20]]
:group-by [:f.a]
:having [:< 0 :f . e ]
2017-07-19 01:01:00 +00:00
:order-by [[:b.baz :desc] :c.quux [:f.a :nulls-first]]
2012-10-19 16:41:26 +00:00
:limit 50
:offset 10}
2017-07-19 05:11:49 +00:00
(sql/format big-complicated-map {:param1 "gabba" :param2 2})
=> [#sql/regularize
"SELECT DISTINCT f.*, b.baz, c.quux, b.bla AS bla_bla, now(), @x := 10
2017-07-19 01:01:00 +00:00
FROM foo f, baz b
2012-10-19 16:41:26 +00:00
INNER JOIN draq ON f.b = draq.x
2017-07-19 01:01:00 +00:00
LEFT JOIN clod c ON f.a = c.d
2012-10-19 16:41:26 +00:00
RIGHT JOIN bock ON bock.z = c.e
WHERE ((f.a = ? AND b.baz < > ?)
2017-07-19 01:01:00 +00:00
OR (? < ? AND ? < ?)
OR (f.e in (?, ?, ?))
OR f.e BETWEEN ? AND ?)
2012-10-19 16:41:26 +00:00
GROUP BY f.a
2017-07-19 01:01:00 +00:00
HAVING ? < f.e
2015-04-16 18:21:21 +00:00
ORDER BY b.baz DESC, c.quux, f.a NULLS FIRST
2017-07-19 01:01:00 +00:00
LIMIT ?
OFFSET ? "
"bort" "gabba" 1 2 2 3 1 2 3 10 20 0 50 10]
2012-07-13 17:09:02 +00:00
2012-07-13 17:13:37 +00:00
;; Printable and readable
2017-07-19 05:11:49 +00:00
(= big-complicated-map (read-string (pr-str big-complicated-map)))
2012-07-13 17:09:02 +00:00
=> true
2012-07-13 15:46:50 +00:00
```
2012-08-24 22:20:58 +00:00
## Extensibility
You can define your own function handlers for use in `where` :
2017-07-19 05:11:49 +00:00
```clojure
2012-08-24 22:20:58 +00:00
(require '[honeysql.format :as fmt])
2012-08-24 22:31:49 +00:00
(defmethod fmt/fn-handler "betwixt" [_ field lower upper]
(str (fmt/to-sql field) " BETWIXT "
2012-08-24 22:20:58 +00:00
(fmt/to-sql lower) " AND " (fmt/to-sql upper)))
2012-08-24 22:31:49 +00:00
(-> (select :a) (where [:betwixt :a 1 10]) sql/format)
2017-07-19 01:01:00 +00:00
=> ["SELECT a WHERE a BETWIXT ? AND ?" 1 10]
2012-08-24 22:31:49 +00:00
2012-08-24 22:20:58 +00:00
```
You can also define your own clauses:
2017-07-19 05:11:49 +00:00
```clojure
2012-08-24 22:20:58 +00:00
;; Takes a MapEntry of the operator & clause data, plus the entire SQL map
(defmethod fmt/format-clause :foobar [[op v] sqlmap]
(str "FOOBAR " (fmt/to-sql v)))
(sql/format {:select [:a :b] :foobar :baz})
=> ["SELECT a, b FOOBAR baz"]
(require '[honeysql.helpers :refer [defhelper]])
2012-08-24 22:37:03 +00:00
;; Defines a helper function, and allows 'build' to recognize your clause
2012-08-24 22:20:58 +00:00
(defhelper foobar [m args]
(assoc m :foobar (first args)))
(-> (select :a :b) (foobar :baz) sql/format)
=> ["SELECT a, b FOOBAR baz"]
```
2019-03-25 21:40:00 +00:00
When adding a new clause, you may also need to register it with a specific priority so that it formats correctly, for example:
```clojure
(fmt/register-clause! :foobar 110)
```
If you do implement a clause or function handler for an ANSI SQL, consider submitting a pull request so others can use it, too. For non-standard clauses and/or functions, look for a library that extends `honeysql` for that specific database or create one, if no such library exists.
2012-10-22 14:20:26 +00:00
2017-08-26 18:39:04 +00:00
## why does my parameter get emitted as `()`?
2019-03-25 21:40:00 +00:00
2017-08-26 18:39:04 +00:00
If you want to use your own datatype as a parameter then the idiomatic approach of implementing `clojure.java.jdbc` 's [`ISQLValue` ](https://clojure.github.io/java.jdbc/#clojure.java.jdbc/ISQLValue ) protocol isn't enough as `honeysql` won't correct pass through your datatype, rather it will interpret it incorrectly.
To teach `honeysql` how to handle your datatype you need to implement [`honeysql.format/ToSql` ](https://github.com/jkk/honeysql/blob/a9dffec632be62c961be7d9e695d0b2b85732c53/src/honeysql/format.cljc#L94 ). For example:
``` clojure
;; given:
(defrecord MyDateWrapper [...]
(to-sql-timestamp [this]...)
)
;; executing:
(hsql/format {:where [:> :some_column (MyDateWrapper. ...)]})
;; results in => "where :some_column > ()"
;; we can teach honeysql about it:
2018-12-30 23:38:17 +00:00
(extend-protocol honeysql.format/ToSql
2017-08-26 18:39:04 +00:00
MyDateWrapper
(to-sql [v] (to-sql (date/to-sql-timestamp v))))
2018-06-25 19:30:48 +00:00
2017-08-26 18:39:04 +00:00
;; allowing us to now:
(hsql/format {:where [:> :some_column (MyDateWrapper. ...)]})
;; which correctly results in => "where :some_column>?" and the parameter correctly set
```
2012-07-13 17:11:46 +00:00
## TODO
* Create table, etc.
2016-07-10 18:43:18 +00:00
## Extensions
* [For PostgreSQL-specific extensions falling outside of ANSI SQL ](https://github.com/nilenso/honeysql-postgres )
2012-07-13 01:50:13 +00:00
## License
2017-08-24 22:58:41 +00:00
Copyright © 2012-2017 Justin Kramer
2012-07-13 01:50:13 +00:00
Distributed under the Eclipse Public License, the same as Clojure.