2021-02-02 22:50:12 +00:00
# SQL Special Syntax
This section lists the function-like expressions that
HoneySQL supports out of the box which are formatted
as special syntactic forms.
2021-02-13 05:50:22 +00:00
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` ).
2021-02-02 22:50:12 +00:00
## array
2021-02-03 00:43:09 +00:00
Accepts a single argument, which is expected to evaluate to
a sequence, and produces `ARRAY[?, ?, ..]` for the elements
of that sequence (as SQL parameters):
```clojure
(sql/format-expr [:array (range 5)])
;;=> ["ARRAY[?, ?, ?, ?, ?]" 0 1 2 3 4]
```
2021-02-02 22:50:12 +00:00
## between
2021-02-03 00:43:09 +00:00
Accepts three arguments: an expression, a lower bound, and
an upper bound:
```clojure
(sql/format-expr [:between :id 1 100])
;;=> ["id BETWEEN ? AND ?" 1 100]
```
2021-02-02 22:50:12 +00:00
## case
2021-02-03 00:43:09 +00:00
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:
```clojure
(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"]
```
2021-02-02 22:50:12 +00:00
## cast
2021-02-03 00:43:09 +00:00
A SQL CAST expression. Expects an expression and something
that produces a SQL type:
```clojure
(sql/format-expr [:cast :a :int])
;;=> ["CAST(a AS int)"]
```
2021-02-02 22:50:12 +00:00
## composite
2021-02-03 00:43:09 +00:00
Accepts any number of expressions and produces a composite
expression (comma-separated, wrapped in parentheses):
```clojure
(sql/format-expr [:composite :a :b "red" [:+ :x 1]])
;;=> ["(a, b, ?, x + ?)" "red" 1]
```
2021-03-12 19:43:21 +00:00
## entity
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.
2021-04-10 00:23:39 +00:00
```clojure
[:tablespace :quux]
;;=> TABLESPACE QUUX
[:tablespace [:entity :quux]]
;;=> TABLESPACE quux
```
2021-03-12 23:39:54 +00:00
## escape
Intended to be used with regular expression patterns to
specify the escape characters (if any).
2021-04-10 00:23:39 +00:00
```clojure
(format {:select :* :from :foo
:where [:similar-to :foo [:escape "bar" [:inline "*"]]]})
;;=> ["SELECT * FROM foo WHERE foo SIMILAR TO ? ESCAPE '* '" "bar"]))))
```
2021-02-02 22:50:12 +00:00
## inline
2021-02-03 00:43:09 +00:00
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`
* keywords and symbols become upper case entities (with `-` replaced by space)
* strings become inline SQL strings (with single quotes)
* a sequence has each element formatted inline and then joined with spaces
* all other values are just rendered via Clojure's `str` function
```clojure
(sql/format {:where [:= :x [:inline "foo"]]})
;;=> ["WHERE x = 'foo'"]
```
2021-02-02 22:50:12 +00:00
## interval
2021-02-03 00:43:09 +00:00
Accepts two arguments: an expression and a keyword (or a symbol)
that represents a time unit. Produces an `INTERVAL` expression:
```clojure
(sql/format-expr [:date_add [:now] [:interval 30 :days]])
;;=> ["DATE_ADD(NOW(), INTERVAL ? DAYS)" 30]
```
2021-03-13 21:13:35 +00:00
## lateral
Accepts a single argument that can be a (`SELECT`) clause or
2021-04-10 00:23:39 +00:00
a (function call) expression. Produces a `LATERAL` subquery
clause based on the `SELECT` clause or the SQL expression.
2021-03-13 21:13:35 +00:00
2021-02-02 22:50:12 +00:00
## lift
2021-02-03 00:43:09 +00:00
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:
```clojure
(sql/format {:where [:= :json-col [:lift {:a 1 :b "two"}]]})
;;=> ["WHERE json_col = ?" {:a 1 :b "two"}]
```
2021-02-14 22:00:28 +00:00
> Note: HoneySQL 1.x used `honeysql.format/value` for this.
2021-02-02 22:50:12 +00:00
## nest
2021-02-03 00:43:09 +00:00
Used to wrap an expression when you want an extra
level of parentheses around it:
```clojure
(sql/format {:where [:= :x 42]})
;;=> ["WHERE x = ?" 42]
(sql/format {:where [:nest [:= :x 42]]})
;;=> ["WHERE (x = ?)" 42]
```
2021-03-15 21:48:28 +00:00
`:nest` is also supported as a SQL clause for the same reason.
2021-02-03 00:43:09 +00:00
2021-02-02 22:50:12 +00:00
## not
2021-02-03 00:43:09 +00:00
Accepts a single expression and formats it with `NOT`
in front of it:
```clojure
(sql/format-expr [:not nil])
;;=> ["NOT NULL"]
(sql/format-expr [:not [:= :x 42]])
;;=> ["NOT x = ?" 42]
```
2021-02-11 00:25:31 +00:00
## over
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.
2021-02-02 22:50:12 +00:00
## param
2021-02-03 00:43:09 +00:00
Used to identify a named parameter in a SQL expression
as an alternative to a keyword (or a symbol) that begins
with `?` :
```clojure
(sql/format {:where [:= :x :?foo]} {:params {:foo 42}})
;;=> ["WHERE x = ?" 42]
(sql/format {:where [:= :x [:param :foo]]} {:params {:foo 42}})
;;=> ["WHERE x = ?" 42]
```
2021-02-02 22:50:12 +00:00
## raw
2021-02-03 00:43:09 +00:00
Accepts a single argument and renders it as literal SQL
in the formatted string:
```clojure
(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):
```clojure
(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:
```clojure
(sql/format {:select [:a [[:raw ["@var := " [:inline "foo"]]]]]})
;;=> ["SELECT a, @var := 'foo'"]
(sql/format {:select [:a [[:raw ["@var := " ["foo"]]]]]})
;;=> ["SELECT a, @var := ?" "foo"]
```
2021-02-13 05:50:22 +00:00
2021-03-15 21:48:28 +00:00
`:raw` is also supported as a SQL clause for the same reason.
2021-02-13 05:50:22 +00:00
## Column Descriptors
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).
### foreign-key, primary-key
If no arguments are provided, these render as just SQL
keywords (uppercase):
```clojure
[:foreign-key] ;=> FOREIGN KEY
[:primary-key] ;=> PRIMARY KEY
```
Otherwise, these render as regular function calls:
```clojure
[:foreign-key :a] ;=> FOREIGN KEY(a)
2021-02-13 18:50:36 +00:00
[:primary-key :x :y] ;=> PRIMARY KEY(x, y)
2021-02-13 05:50:22 +00:00
```
2021-04-10 00:23:39 +00:00
### constraint, default, references
2021-02-13 05:50:22 +00:00
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:
```clojure
[:default] ;=> DEFAULT
[:default 42] ;=> DEFAULT 42
[:default "str"] ;=> DEFAULT 'str'
[:constraint :name] ;=> CONSTRAINT name
[:references :foo :bar] ;=> REFERENCES foo(bar)
```
2021-04-10 00:23:39 +00:00
### index, unique
2021-02-13 05:50:22 +00:00
These behave like the group above except that if the
first argument is `nil` , it is omitted:
```clojure
2021-02-13 18:50:36 +00:00
[:index :foo :bar :quux] ;=> INDEX foo(bar, quux)
[:index nil :bar :quux] ;=> INDEX(bar, quux)
2021-02-13 05:50:22 +00:00
[:unique :a :b] ;=> UNIQUE a(b)
```