This section lists the function-like expressions that HoneySQL supports out of the box which are formatted as special syntactic forms.
The first group are used for SQL expressions. The second (last group) are used primarily in column definitions (as part of :with-columns
and :add-column
/ :modify-column
).
Accepts a single argument, which is expected to evaluate to
a sequence, and produces ARRAY[?, ?, ..]
for the elements
of that sequence (as SQL parameters):
(require '[honey.sql :as sql])
(sql/format-expr [:array (range 5)])
;;=> ["ARRAY[?, ?, ?, ?, ?]" 0 1 2 3 4]
Note: you cannot provide a named parameter as the argument for
:array
because the generated SQL depends on the number of elements in the sequence, so the following throws an exception:
(sql/format {:select [[[:array :?tags] :arr]]} {:params {:tags [1 2 3]}})
You can do the following instead:
(let [tags [1 2 3]]
(sql/format {:select [[[:array tags] :arr]]} {:inline true}))
;;=> ["SELECT ARRAY[1, 2, 3] AS arr"]
In addition, the argument to :array
is treated as a literal sequence of Clojure values and is not interpreted as a HoneySQL expression, so you must use the {:inline true}
formatting option as shown above rather than try to inline the values like this:
(sql/format {:select [[[:array [:inline [1 2 3]]] :arr]]})
;;=> ["SELECT ARRAY[inline, (?, ?, ?)] AS arr" 1 2 3]
Accepts three arguments: an expression, a lower bound, and an upper bound:
(sql/format-expr [:between :id 1 100])
;;=> ["id BETWEEN ? AND ?" 1 100]
A SQL CASE expression. Expects an even number of arguments:
alternating condition and result expressions. A condition
may be :else
(or 'else
) to produce ELSE
, otherwise
WHEN <condition> THEN <result>
will be produced:
(sql/format-expr [:case [:< :a 10] "small" [:> :a 100] "big" :else "medium"])
;; => ["CASE WHEN a < ? THEN ? WHEN a > ? THEN ? ELSE ? END" 10 "small" 100 "big" "medium"]
Google BigQuery supports a variant of CASE
that takes an expression and then the WHEN
clauses contain expressions to match against, rather than conditions. HoneySQL supports
this using :case-expr
:
(sql/format-expr [:case-expr :a 10 "small" 100 "big" :else "medium"])
;; => ["CASE a WHEN ? THEN ? WHEN ? THEN ? ELSE ? END" 10 "small" 100 "big" "medium"]
A SQL CAST expression. Expects an expression and something that produces a SQL type:
(sql/format-expr [:cast :a :int])
;;=> ["CAST(a AS int)"]
Accepts any number of expressions and produces a composite expression (comma-separated, wrapped in parentheses):
(sql/format-expr [:composite :a :b "red" [:+ :x 1]])
;;=> ["(a, b, ?, x + ?)" "red" 1]
Accepts a single expression and prefixes it with DISTINCT
:
(sql/format {:select [ [[:count [:distinct :status]] :n] ] :from :table})
;;=> ["SELECT COUNT(DISTINCT status) AS n FROM table"]
Accepts a single keyword or symbol argument and produces a SQL entity. This is intended for use in contexts that would otherwise produce a sequence of SQL keywords, such as when constructing DDL statements.
[:tablespace :quux]
;;=> TABLESPACE QUUX
[:tablespace [:entity :quux]]
;;=> TABLESPACE quux
Intended to be used with regular expression patterns to specify the escape characters (if any).
(sql/format {:select :* :from :foo
:where [:similar-to :foo [:escape "bar" [:inline "*"]]]})
;;=> ["SELECT * FROM foo WHERE foo SIMILAR TO ? ESCAPE '*'" "bar"]
Used to produce PostgreSQL's FILTER
and WITHIN GROUP
expressions.
See also order-by below.
These both accept a SQL expression followed by a SQL clause.
Filter generally expects an aggregate expression and a WHERE
clause.
Within group generally expects an aggregate expression and an ORDER BY
clause.
(sql/format {:select [:a :b [[:filter :%count.* {:where [:< :x 100]}] :c]
[[:within-group [:percentile_disc [:inline 0.25]]
{:order-by [:a]}] :inter_max]
[[:within-group [:percentile_cont [:inline 0.25]]
{:order-by [:a]}] :abs_max]]
:from :aa}
{:pretty true})
;;=> ["
SELECT a, b, COUNT(*) FILTER (WHERE x < ?) AS c, PERCENTILE_DISC(0.25) WITHIN GROUP (ORDER BY a ASC) AS inter_max, PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY a ASC) AS abs_max
FROM aa
"
100]
There are helpers for both filter
and within-group
. Be careful with filter
since it shadows clojure.core/filter
:
(refer-clojure :exclude '[filter])
(require '[honey.sql.helpers :refer [select filter within-group from order-by where]])
(sql/format (-> (select :a :b [(filter :%count.* (where :< :x 100)) :c]
[(within-group [:percentile_disc [:inline 0.25]]
(order-by :a)) :inter_max]
[(within-group [:percentile_cont [:inline 0.25]]
(order-by :a)) :abs_max])
(from :aa))
{:pretty true})
;;=> ["
SELECT a, b, COUNT(*) FILTER (WHERE x < ?) AS c, PERCENTILE_DISC(0.25) WITHIN GROUP (ORDER BY a ASC) AS inter_max, PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY a ASC) AS abs_max
FROM aa
"
100]
Accepts a single argument and tries to render it as a SQL value directly in the formatted SQL string rather than turning it into a positional parameter:
nil
becomes NULL
-
replaced by space)str
function(sql/format {:where [:= :x [:inline "foo"]]})
;;=> ["WHERE x = 'foo'"]
Accepts two arguments: an expression and a keyword (or a symbol)
that represents a time unit. Produces an INTERVAL
expression:
(sql/format-expr [:date_add [:now] [:interval 30 :days]])
;;=> ["DATE_ADD(NOW(), INTERVAL ? DAYS)" 30]
Accepts a single argument that can be a (SELECT
) clause or
a (function call) expression. Produces a LATERAL
subquery
clause based on the SELECT
clause or the SQL expression.
Used to wrap a Clojure value that should be passed as a SQL parameter but would otherwise be treated as a SQL expression or statement, i.e., a sequence or hash map. This can be useful when dealing with JSON types:
(sql/format {:where [:= :json-col [:lift {:a 1 :b "two"}]]})
;;=> ["WHERE json_col = ?" {:a 1 :b "two"}]
Note: HoneySQL 1.x used
honeysql.format/value
for this.
Used to wrap an expression when you want an extra level of parentheses around it:
(sql/format {:where [:= :x 42]})
;;=> ["WHERE x = ?" 42]
(sql/format {:where [:nest [:= :x 42]]})
;;=> ["WHERE (x = ?)" 42]
:nest
is also supported as a SQL clause for the same reason.
;; BigQuery requires UNION clauses be parenthesized:
(sql/format {:union-all [{:nest {:select :*}} {:nest {:select :*}}]})
;;=> ["(SELECT *) UNION ALL (SELECT *)"]
Accepts a single expression and formats it with NOT
in front of it:
(sql/format-expr [:not nil])
;;=> ["NOT NULL"]
(sql/format-expr [:not [:= :x 42]])
;;=> ["NOT (x = ?)" 42]
In addition to the ORDER BY
clause, HoneySQL also supports ORDER BY
in an expression (for PostgreSQL). It accepts a SQL expression followed
by an ordering specifier, which can be an expression or a pair of expression
and direction (:asc
or :desc
):
(sql/format {:select [[[:array_agg [:order-by :a [:b :desc]]]]] :from :table})
;;=> ["SELECT ARRAY_AGG(a ORDER BY b DESC) FROM table"]
(sql/format (-> (select [[:array_agg [:order-by :a [:b :desc]]]])
(from :table)))
;;=> ["SELECT ARRAY_AGG(a ORDER BY b DESC) FROM table"]
(sql/format {:select [[[:string_agg :a [:order-by [:inline ","] :a]]]] :from :table})
;;=> ["SELECT STRING_AGG(a, ',' ORDER BY a ASC) FROM table"]
(sql/format (-> (select [[:string_agg :a [:order-by [:inline ","] :a]]])
(from :table)))
;;=> ["SELECT STRING_AGG(a, ',' ORDER BY a ASC) FROM table"]
There is no helper for the ORDER BY
special syntax: the order-by
helper
only produces a SQL clause.
This is intended to be used with the :window
and :partition-by
clauses.
:over
takes any number of window expressions which are either pairs or triples
that have an aggregation expression, a window function, and an optional alias.
The window function may either be a SQL entity (named in a :window
clause)
or a SQL clause that describes the window (e.g., using :partition-by
and/or :order-by
).
Since a function call (using :over
) needs to be wrapped in a sequence for a
:select
clause, it is usually easier to use the over
helper function
to construct this expression.
Used to identify a named parameter in a SQL expression
as an alternative to a keyword (or a symbol) that begins
with ?
:
(sql/format {:where [:= :x :?foo]} {:params {:foo 42}})
;;=> ["WHERE x = ?" 42]
(sql/format {:where [:= :x [:param :foo]]} {:params {:foo 42}})
;;=> ["WHERE x = ?" 42]
Accepts a single argument and renders it as literal SQL in the formatted string:
(sql/format {:select [:a [[:raw "@var := foo"]]]})
;;=> ["SELECT a, @var := foo"]
If the argument is a sequence of expressions, they will each be rendered literally and joined together (with no spaces):
(sql/format {:select [:a [[:raw ["@var" " := " "foo"]]]]})
;;=> ["SELECT a, @var := foo"]
When a sequence of expressions is supplied, any subexpressions that are, in turn, sequences will be formatted as regular SQL expressions and that SQL will be joined into the result, along with any parameters from them:
(sql/format {:select [:a [[:raw ["@var := " [:inline "foo"]]]]]})
;;=> ["SELECT a, @var := 'foo'"]
(sql/format {:select [:a [[:raw ["@var := " ["foo"]]]]]})
;;=> ["SELECT a, @var := (?)" "foo"]
:raw
is also supported as a SQL clause for the same reason.
There are three types of descriptors that vary in how they treat their first argument. All three descriptors automatically try to inline any parameters (and will throw an exception if they can't, since these descriptors are meant to be used in column or index specifications).
If no arguments are provided, these render as just SQL keywords (uppercase):
[:foreign-key] ;=> FOREIGN KEY
[:primary-key] ;=> PRIMARY KEY
Otherwise, these render as regular function calls:
[:foreign-key :a] ;=> FOREIGN KEY(a)
[:primary-key :x :y] ;=> PRIMARY KEY(x, y)
Although these are grouped together, they are generally used differently. This group renders as SQL keywords if no arguments are provided. If a single argument is provided, this renders as a SQL keyword followed by the argument. If two or more arguments are provided, this renders as a SQL keyword followed by the first argument, followed by the rest as a regular argument list:
[:default] ;=> DEFAULT
[:default 42] ;=> DEFAULT 42
[:default "str"] ;=> DEFAULT 'str'
[:constraint :name] ;=> CONSTRAINT name
[:references :foo :bar] ;=> REFERENCES foo(bar)
These behave like the group above except that if the
first argument is nil
, it is omitted:
[:index :foo :bar :quux] ;=> INDEX foo(bar, quux)
[:index nil :bar :quux] ;=> INDEX(bar, quux)
[:unique :a :b] ;=> UNIQUE a(b)
Can you improve this documentation? These fine people already did:
Sean Corfield & lreadEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close