Overhaul differences document
This commit is contained in:
parent
d7c7f15a6b
commit
1b749c1599
3 changed files with 60 additions and 15 deletions
|
|
@ -591,7 +591,7 @@ If your database supports `<=>` as an operator, you can tell HoneySQL about it u
|
|||
(-> (select :a) (where [:<=> :a "foo"]) sql/format)
|
||||
=> ["SELECT a WHERE a <=> ?" "foo"]
|
||||
;; you can declare that an operator is variadic:
|
||||
(sql/register-op! :<=> :variadic? true)
|
||||
(sql/register-op! :<=> :variadic true)
|
||||
(-> (select :a) (where [:<=> "food" :a "fool"]) sql/format)
|
||||
=> ["SELECT a WHERE ? <=> a <=> ?" "food" "fool"]
|
||||
```
|
||||
|
|
@ -599,7 +599,7 @@ If your database supports `<=>` as an operator, you can tell HoneySQL about it u
|
|||
Sometimes you want an operator to ignore `nil` clauses (`:and` and `:or` are declared that way):
|
||||
|
||||
```clojure
|
||||
(sql/register-op! :<=> :ignore-nil? true)
|
||||
(sql/register-op! :<=> :ignore-nil true)
|
||||
```
|
||||
|
||||
Or perhaps your database supports syntax like `a BETWIXT b AND c`, in which case you can use `register-fn!` to tell HoneySQL about it (again, called before the first call to `honey.sql/format`):
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ The DSL itself -- the data structures that both versions convert to SQL and para
|
|||
|
||||
HoneySQL 2.x uses the group ID `seancorfield` with the original artifact ID of `honeysql`, in line with the recommendations in Inside Clojure's post about the changes in the Clojure CLI: [Deprecated unqualified lib names](https://insideclojure.org/2020/07/28/clj-exec/).
|
||||
|
||||
In addition, HoneySQL 2.x contains different namespaces so you can have both versions on your classpath without introducing any conflicts. The primary API is now in `honey.sql` and the helpers are in `honey.sql.helpers`. A Spec for the DSL data structure is available in `honey.specs` (work in progress).
|
||||
In addition, HoneySQL 2.x contains different namespaces so you can have both versions on your classpath without introducing any conflicts. The primary API is now in `honey.sql` and the helpers are in `honey.sql.helpers`. A Spec for the DSL data structure will be available in `honey.specs` at some point (work in progress).
|
||||
|
||||
### HoneySQL 1.x
|
||||
|
||||
|
|
@ -27,6 +27,8 @@ honeysql/honeysql {:mvn/version "1.0.444"}
|
|||
...
|
||||
(sql/format {:select [:*] :from [:table] :where [:= :id 1]})
|
||||
;;=> ["SELECT * FROM table WHERE id = ?" 1]
|
||||
(sql/format {:select [:*] :from [:table] :where [:= :id 1]} :quoting :mysql)
|
||||
;;=> ["SELECT * FROM `table` WHERE `id` = ?" 1]
|
||||
```
|
||||
|
||||
The namespaces were:
|
||||
|
|
@ -36,6 +38,8 @@ The namespaces were:
|
|||
* `honeysql.types` -- records, protocols, and data readers,
|
||||
* `honeysql.util` -- internal utilities (macros).
|
||||
|
||||
Supported Clojure versions: 1.7 and later.
|
||||
|
||||
### HoneySQL 2.x
|
||||
|
||||
```clojure
|
||||
|
|
@ -49,6 +53,8 @@ seancorfield/honeysql {:mvn/version "2.x"}
|
|||
...
|
||||
(sql/format {:select [:*] :from [:table] :where [:= :id 1]})
|
||||
;;=> ["SELECT * FROM table WHERE id = ?" 1]
|
||||
(sql/format {:select [:*] :from [:table] :where [:= :id 1]} {:dialect :mysql})
|
||||
;;=> ["SELECT * FROM `table` WHERE `id` = ?" 1]
|
||||
```
|
||||
|
||||
The new namespaces are:
|
||||
|
|
@ -56,15 +62,17 @@ The new namespaces are:
|
|||
* `honey.sql.helpers` -- helper functions to build the DSL,
|
||||
* `honey.specs` -- a description of the DSL using `clojure.spec.alpha`.
|
||||
|
||||
Supported Clojure versions: 1.9 and later.
|
||||
|
||||
## API Changes
|
||||
|
||||
The primary API is just `honey.sql/format`. The `array`, `call`, `inline`, `param`, and `raw` functions have all become standard syntax in the DSL as functions (and their tagged literal equivalents have also gone away because they are no longer needed).
|
||||
|
||||
Other `honeysql.core` functions that no longer exist include: `build`, `qualify`, and `quote-identifier`. Many other public functions were essentially undocumented (neither mentioned in the README nor in the tests) and also no longer exist.
|
||||
|
||||
You can now select a non-ANSI dialect of SQL using the new `honey.sql/set-dialect!` function (which sets a default dialect for all `format` operations) or by passing the new `:dialect` option to the `format` function. `:ansi` is the default dialect (which will mostly incorporate PostgreSQL usage over time). Other dialects supported are `:mysql` (which has a different quoting strategy and uses a different ranking for the `:set` clause), `:oracle` (which is essentially the `:ansi` dialect but will control other things over time), and `:sqlserver` (which is essentially the `:ansi` dialect but with a different quoting strategy) and . Other dialects and changes may be added over time.
|
||||
You can now select a non-ANSI dialect of SQL using the new `honey.sql/set-dialect!` function (which sets a default dialect for all `format` operations) or by passing the new `:dialect` option to the `format` function. `:ansi` is the default dialect (which will mostly incorporate PostgreSQL usage over time). Other dialects supported are `:mysql` (which has a different quoting strategy and uses a different ranking for the `:set` clause), `:oracle` (which is essentially the `:ansi` dialect but will control other things over time), and `:sqlserver` (which is essentially the `:ansi` dialect but with a different quoting strategy). Other dialects and changes may be added over time.
|
||||
|
||||
> Note: `:limit` and `:offset` are currently in the default `:ansi` dialect even though they are MySQL-specific. This is temporary as the dialects are being fleshed out. I expect to add `:top` for `:sqlserver` and `:offset` / `:fetch` for `:ansi`, at which point `:limit` / `:offset` will become MySQL-only.
|
||||
> Note: `:limit` and `:offset` are currently in the default `:ansi` dialect even though they are MySQL-specific. This will change as the dialects are fleshed out. I plan to add `:top` for `:sqlserver` and `:offset` / `:fetch` for `:ansi`, at which point `:limit` / `:offset` will become MySQL-only.
|
||||
|
||||
## Option Changes
|
||||
|
||||
|
|
@ -75,11 +83,36 @@ The `:quoting <dialect>` option has superseded by the new dialect machinery and
|
|||
Identifiers are automatically quoted if you specify a `:dialect` option to `format`, unless you also specify `:quoted false`.
|
||||
|
||||
The following options are no longer supported:
|
||||
* `:namespace-as-table?` -- TODO
|
||||
* `:allow-dashed-names?` -- if you provide dashed-names in v2, they will be left as-is if quoting is enabled, else they will be converted to snake_case (so you will either get `"dashed-names"` with quoting or `dashed_names` without); v1 treated names specified as keywords and names specified as strings differently.
|
||||
* `:allow-namespaced-names?` -- this supported `foo/bar` column names in SQL which I'd like to discourage.
|
||||
* `:namespace-as-table?` -- this is the default in v2: `:foo/bar` will be treated as `foo.bar` which is more in keeping with `next.jdbc`.
|
||||
* `:parameterizer` -- this would add a lot of complexity to the formatting engine and I do not know how widely it was used (especially in its arbitrarily extensible form).
|
||||
* `:return-param-names` -- this was added to v1 back in 2013 without an associated issue or PR so I've no idea what use case this was intended to support.
|
||||
|
||||
> Note: I expect some push back on those first three options and the associated behavior changes.
|
||||
|
||||
## DSL Changes
|
||||
|
||||
The general intent is that the data structure behind the DSL is unchanged, for the most part. The only deliberate change is the removal of the reader literals (and their associated helper functions) in favor of standardized syntax, e.g., `[:array [1 2 3]]` instead of either `#sql/array [1 2 3]` or `(sql/array [1 2 3])`.
|
||||
|
||||
The following new syntax has been added:
|
||||
|
||||
* `:array` -- used as a function to replace the `sql/array` / `#sql/array` machinery,
|
||||
* `:between` -- this is now explicit syntax rather than being a special case in expressions,
|
||||
* `:case` -- this is now explicit syntax,
|
||||
* `:cast` -- `[:cast expr :type]` => `CAST( expr AS type )`,
|
||||
* `:composite` -- explicit syntax to produce a comma-separated list of expressions, wrapped in parentheses,
|
||||
* `:default` -- for `DEFAULT` values (in inserts),
|
||||
* `:inline` -- used as a function to replace the `sql/inline` / `#sql/inline` machinery,
|
||||
* `:interval` -- used as a function to support `INTERVAL <n> <units>`, e.g., `[:interval 30 :days]`.
|
||||
* `:lift` -- used as a function to prevent interpretation of a Clojure data structure as DSL syntax (e.g., when passing a vector or hash map as a parameter value),
|
||||
* `:nest` -- used as a function to add an extra level of nesting (parentheses) around an expression,
|
||||
* `:not` -- this is now explicit syntax,
|
||||
* `:param` -- used as a function to replace the `sql/param` / `#sql/param` machinery,
|
||||
* `:raw` -- used as a function to replace the `sql/raw` / `#sql/raw` machinery. Vector subexpressions inside a `[:raw ..]` expression are formatted to SQL and parameters. Other subexpressions are just turned into strings and concatenated. This is different to the v1 behavior but should be more flexible, since you can now embed `:inline`, `:param`, and `:lift` inside a `:raw` expression.
|
||||
|
||||
> Note 1: in 1.x, inlining a string `"foo"` produced `foo` but in 2.x it produces `'foo'`, i.e., string literals become SQL strings without needing internal quotes (1.x required `"'foo'"`).
|
||||
|
||||
You can now `SELECT` a function call more easily, using `[[...]]`. This was previously an error -- missing an alias -- but it was a commonly requested change, to avoid using `(sql/call ...)`:
|
||||
|
||||
```clojure
|
||||
|
|
@ -89,17 +122,23 @@ You can now `SELECT` a function call more easily, using `[[...]]`. This was prev
|
|||
|
||||
```
|
||||
|
||||
On a related note, `sql/call` has been removed because it should never be needed now: `[:foo ...]` should always be treated as a function call, consistently, avoiding the special cases in v1 that necessitated the explicit `sql/call` syntax.
|
||||
|
||||
The `:set` clause is dialect-dependent. In `:mysql`, it is ranked just before the `:where` clause. In all other dialects, it is ranked just before the `:from` clause. Accordingly, the `:set0` and `:set1` clauses are no longer supported (because they were workarounds in 1.x for this conflict).
|
||||
|
||||
The following new syntax has been added:
|
||||
|
||||
* `:array` -- used as a function to replace the `sql/array` / `#sql/array` machinery,
|
||||
* `:inline` -- used as a function to replace the `sql/inline` / `#sql/inline` machinery,
|
||||
* `:interval` -- used as a function to support `INTERVAL <n> <units>`, e.g., `[:interval 30 :days]`.
|
||||
* `:raw` -- used as a function to replace the `sql/raw` / `#sql/raw` machinery. Vector subexpressions inside a `[:raw ..]` expression are formatted to SQL and parameters. Other subexpressions are just turned into strings and concatenated. This is different to the v1 behavior but should be more flexible.
|
||||
|
||||
> Note 1: in 1.x, inlining a string `"foo"` produced `foo` but in 2.x it produces `'foo'`, i.e., string literals become SQL strings without needing internal quotes (1.x required `"'foo'"`).
|
||||
|
||||
## Extensibility
|
||||
|
||||
The protocols and multimethods in 1.x have all gone away. The primary extension point is `honey.sql/register-clause!` which lets you specify the new clause (keyword), the formatter function for it, and the existing clause that it should be ranked before (`format` processes the DSL in clause order).
|
||||
|
||||
You can also register new "functions" that can implement special syntax (such as `:array`, `:inline`, `:raw` etc above) via `honey.sql/register-fn!`. This accepts a "function" name as a keyword and a formatter which will generally be a function of two arguments: the function name (so formatters can be reused across different names) and a vector of the arguments the function should accept.
|
||||
|
||||
And, finally, you can register new operators that will be recognized in expressions via `honey.sql/register-op!`. This accepts an operator name as a keyword and optional named parameters to indicate whether the operator is `:variadic` (the default is strictly binary) and whether it should ignore operands that evaluate to `nil` (via `:ignore-nil`). The latter can make it easier to construct complex expressions programmatically without having to worry about conditionally removing "optional" (`nil`) values.
|
||||
|
||||
## Helpers
|
||||
|
||||
The `honey.sql.helpers` namespace includes a helper function that corresponds to every supported piece of the data DSL understood by HoneySQL (v1 only had a limited set of helper functions). Unlike v1 helpers which sometimes had both a regular helper and a `merge-` helper, v2 helpers will all merge clauses by default (if that makes sense for the underlying DSL): use `:dissoc` if you want to force an overwrite.
|
||||
|
||||
The only helpers that have non-merging behavior are:
|
||||
* `intersect`, `union`, `union-all`, `except`, and `except-all` which always wrap around their arguments,
|
||||
* `delete`, `set`, `limit`, `offset`, `for`, and `values` which overwrite, rather than merge,
|
||||
* `composite` which is a convenience for the `:composite` syntax mentioned above: `(composite :a :b)` is the same as `[:composite :a :b]` which produces `(a, b)`.
|
||||
|
|
|
|||
|
|
@ -68,3 +68,9 @@
|
|||
base
|
||||
(apply build (apply concat base)))
|
||||
(partition 2 clauses))))
|
||||
|
||||
(comment
|
||||
(require '[honeysql.core :as sql])
|
||||
(sql/format {:select [:*] :from [:table] :where [:= :id 1]})
|
||||
(sql/format {:select [:*] :from [:table] :where [:= :id 1]} :quoting :mysql)
|
||||
,)
|
||||
|
|
|
|||
Loading…
Reference in a new issue