Merge pull request #446 from seancorfield/issue-405
Fix Issue 405 by adding :numbered true option
This commit is contained in:
commit
295be57bfc
9 changed files with 225 additions and 41 deletions
|
|
@ -1,10 +1,12 @@
|
|||
# Changes
|
||||
|
||||
* 2.4.next in progress
|
||||
* Fix `set-options!` (only `:checking` worked in 2.4.947).
|
||||
* Fix `:cast` formatting when quoting is enabled, via PR [#443](https://github.com/seancorfield/honeysql/pull/443) [duddlf23](https://github.com/duddlf23).
|
||||
* Fix [#441](https://github.com/seancorfield/honeysql/issues/441) by adding `:replace-into` to in-flight clause order (as well as registering it for the `:mysql` dialect).
|
||||
* Fix [#434](https://github.com/seancorfield/honeysql/issues/434) by special-casing `:'ARRAY`.
|
||||
* Fix [#433](https://github.com/seancorfield/honeysql/issues/433) by supporting additional `WITH` syntax, via PR [#432](https://github.com/seancorfield/honeysql/issues/432), [@MawiraIke](https://github.com/MawiraIke). _[Technically, this was in 2.4.947, but I kept the issue open while I wordsmithed the documentation]_
|
||||
* Address [#405](https://github.com/seancorfield/honeysql/issues/405) by adding `:numbered` option, which can also be set globally using `set-options!`.
|
||||
|
||||
* 2.4.947 -- 2022-11-05
|
||||
* Fix [#439](https://github.com/seancorfield/honeysql/issues/439) by rewriting how DDL options are processed; also fixes [#386](https://github.com/seancorfield/honeysql/issues/386) and [#437](https://github.com/seancorfield/honeysql/issues/437); **Whilst this is intended to be purely a bug fix, it has the potential to be a breaking change -- hence the version jump to 2.4!**
|
||||
|
|
|
|||
51
README.md
51
README.md
|
|
@ -116,6 +116,13 @@ If you want to format the query as a string with no parameters (e.g. to use the
|
|||
=> ["SELECT a, b, c FROM foo WHERE foo.a = 'baz'"]
|
||||
```
|
||||
|
||||
As seen above, the default parameterization uses positional parameters (`?`) with the order of values in the generated vector matching the order of those placeholders in the SQL. As of 2.4.next, you can specified `:numbered true` as an option to produce numbered parameters (`$1`, `$2`, etc):
|
||||
|
||||
```clojure
|
||||
(sql/format sqlmap {:numbered true})
|
||||
=> ["SELECT a, b, c FROM foo WHERE foo.a = $1" "baz"]
|
||||
```
|
||||
|
||||
Namespace-qualified keywords (and symbols) are generally treated as table-qualified columns: `:foo/bar` becomes `foo.bar`, except in contexts where that would be illegal (such as the list of columns in an `INSERT` statement). This approach is likely to be more compatible with code that uses libraries like [`next.jdbc`](https://github.com/seancorfield/next-jdbc) and [`seql`](https://github.com/exoscale/seql), as well as being more convenient in a world of namespace-qualified keywords, following the example of `clojure.spec` etc.
|
||||
|
||||
```clojure
|
||||
|
|
@ -388,6 +395,19 @@ INSERT INTO comp_table
|
|||
VALUES (?, (?, ?)), (?, (?, ?))
|
||||
"
|
||||
"small" 1 "inch" "large" 10 "feet"]
|
||||
;; with numbered parameters:
|
||||
(-> (insert-into :comp_table)
|
||||
(columns :name :comp_column)
|
||||
(values
|
||||
[["small" (composite 1 "inch")]
|
||||
["large" (composite 10 "feet")]])
|
||||
(sql/format {:pretty true :numbered true}))
|
||||
=> ["
|
||||
INSERT INTO comp_table
|
||||
(name, comp_column)
|
||||
VALUES ($1, ($2, $3)), ($4, ($5, $6))
|
||||
"
|
||||
"small" 1 "inch" "large" 10 "feet"]
|
||||
;; or as pure data DSL:
|
||||
(-> {:insert-into [:comp_table],
|
||||
:columns [:name :comp_column],
|
||||
|
|
@ -608,6 +628,12 @@ Keywords that begin with `?` are interpreted as bindable parameters:
|
|||
(where [:= :a :?baz])
|
||||
(sql/format {:params {:baz "BAZ"}}))
|
||||
=> ["SELECT id FROM foo WHERE a = ?" "BAZ"]
|
||||
;; or with numbered parameters:
|
||||
(-> (select :id)
|
||||
(from :foo)
|
||||
(where [:= :a :?baz])
|
||||
(sql/format {:params {:baz "BAZ"} :numbered true}))
|
||||
=> ["SELECT id FROM foo WHERE a = $1" "BAZ"]
|
||||
;; or as pure data DSL:
|
||||
(-> {:select [:id], :from [:foo], :where [:= :a :?baz]}
|
||||
(sql/format {:params {:baz "BAZ"}}))
|
||||
|
|
@ -832,6 +858,24 @@ LIMIT ?
|
|||
OFFSET ?
|
||||
"
|
||||
"bort" "gabba" 1 2 2 3 1 2 3 10 20 0 50 10]
|
||||
;; with numbered parameters:
|
||||
(sql/format big-complicated-map
|
||||
{:params {:param1 "gabba" :param2 2}
|
||||
:pretty true :numbered true})
|
||||
=> ["
|
||||
SELECT DISTINCT f.*, b.baz, c.quux, b.bla AS \"bla-bla\", NOW(), @x := 10
|
||||
FROM foo AS f, baz AS b
|
||||
INNER JOIN draq ON f.b = draq.x INNER JOIN eldr ON f.e = eldr.t
|
||||
LEFT JOIN clod AS c ON f.a = c.d
|
||||
RIGHT JOIN bock ON bock.z = c.e
|
||||
WHERE ((f.a = $1) AND (b.baz <> $2)) OR (($3 < $4) AND ($5 < $6)) OR (f.e IN ($7, $8, $9)) OR f.e BETWEEN $10 AND $11
|
||||
GROUP BY f.a, c.e
|
||||
HAVING $12 < f.e
|
||||
ORDER BY b.baz DESC, c.quux ASC, f.a NULLS FIRST
|
||||
LIMIT $13
|
||||
OFFSET $14
|
||||
"
|
||||
"bort" "gabba" 1 2 2 3 1 2 3 10 20 0 50 10]
|
||||
```
|
||||
```clojure
|
||||
;; Printable and readable
|
||||
|
|
@ -882,8 +926,13 @@ Or perhaps your database supports syntax like `a BETWIXT b AND c`, in which case
|
|||
;; example usage:
|
||||
(-> (select :a) (where [:betwixt :a 1 10]) sql/format)
|
||||
=> ["SELECT a WHERE a BETWIXT ? AND ?" 1 10]
|
||||
;; with numbered parameters:
|
||||
(-> (select :a) (where [:betwixt :a 1 10]) (sql/format {:numbered true}))
|
||||
=> ["SELECT a WHERE a BETWIXT $1 AND $2" 1 10]
|
||||
```
|
||||
|
||||
> Note: the generation of positional placeholders (`?`) or numbered placeholders (`$1`, `$2`, etc) is handled automatically by `format-expr` so you get this behavior "for free" in your extensions, as long as you use the public API for `honey.sql`. You should avoid writing extensions that generate placeholders directly if you want them to work with numbered parameters.
|
||||
|
||||
You can also register SQL clauses, specifying the keyword, the formatting function, and an existing clause that this new clause should be processed before:
|
||||
|
||||
```clojure
|
||||
|
|
@ -909,6 +958,6 @@ If you find yourself registering an operator, a function (syntax), or a new clau
|
|||
|
||||
## License
|
||||
|
||||
Copyright (c) 2020-2021 Sean Corfield. HoneySQL 1.x was copyright (c) 2012-2020 Justin Kramer and Sean Corfield.
|
||||
Copyright (c) 2020-2022 Sean Corfield. HoneySQL 1.x was copyright (c) 2012-2020 Justin Kramer and Sean Corfield.
|
||||
|
||||
Distributed under the Eclipse Public License, the same as Clojure.
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ You can now select a non-ANSI dialect of SQL using the new `honey.sql/set-dialec
|
|||
|
||||
## Option Changes
|
||||
|
||||
The `:quoting <dialect>` option has superseded by the new dialect machinery and a new `:quoted` option that turns quoting on or off. You either use `:dialect <dialect>` instead or set a default dialect (via `set-dialect!`) and then use `:quoted true` in `format` calls where you want quoting.
|
||||
The `:quoting <dialect>` option has been superseded by the new dialect machinery and a new `:quoted` option that turns quoting on or off. You either use `:dialect <dialect>` instead (which turns on quoting by default) or set a default dialect (via `set-dialect!`) and then use `:quoted true` in `format` calls where you want quoting.
|
||||
|
||||
SQL entity names are automatically quoted if you specify a `:dialect` option to `format`, unless you also specify `:quoted false`.
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ The following options are no longer supported:
|
|||
* `:allow-dashed-names?` -- if you provide dashed-names in 2.x, 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). If you want dashed-names to be converted to snake_case when `:quoted true`, you also need to specify `:quoted-snake true`.
|
||||
* `: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 2.x: `: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).
|
||||
* `: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). _[As of 2.4.next, the ability to generated SQL with numbered parameters, i.e., `$1` instead of positional parameters, `?`, has been added via the `:numbered true` option]_
|
||||
* `:return-param-names` -- this was added to 1.x 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.
|
||||
|
|
|
|||
|
|
@ -110,6 +110,8 @@ Some "functions" are considered to be operators. In general,
|
|||
`42` and `"c"` lifted out into the overall vector result
|
||||
(with a SQL string followed by all its parameters).
|
||||
|
||||
> Note: you can use the `:numbered true` option to `format` to produce SQL containing numbered placeholders, like `FOO(a, $1, $2)`, instead of positional placeholders (`?`).
|
||||
|
||||
Operators can be strictly binary or variadic (most are strictly binary).
|
||||
Special syntax can have zero or more arguments and each form is
|
||||
described in the [Special Syntax](special-syntax.md) section.
|
||||
|
|
@ -179,7 +181,7 @@ expression requires an extra level of nesting:
|
|||
|
||||
As indicated in the preceding sections, values found in the DSL data structure
|
||||
that are not keywords or symbols are lifted out as positional parameters.
|
||||
They are replaced by `?` in the generated SQL string and added to the
|
||||
By default, they are replaced by `?` in the generated SQL string and added to the
|
||||
parameter list in order:
|
||||
|
||||
<!-- :test-doc-blocks/skip -->
|
||||
|
|
@ -187,6 +189,14 @@ parameter list in order:
|
|||
[:between :size 10 20] ;=> "size BETWEEN ? AND ?" with parameters 10 and 20
|
||||
```
|
||||
|
||||
If you specify the `:numbered true` option to `format`, numbered placeholders (`$1`, `$2`, etc) will be used instead of positional placeholders (`?`).
|
||||
|
||||
<!-- :test-doc-blocks/skip -->
|
||||
```clojure
|
||||
;; with :numbered true option:
|
||||
[:between :size 10 20] ;=> "size BETWEEN $1 AND $2" with parameters 10 and 20
|
||||
```
|
||||
|
||||
HoneySQL also supports named parameters. There are two ways
|
||||
of identifying a named parameter:
|
||||
* a keyword or symbol that begins with `?`
|
||||
|
|
@ -206,6 +216,18 @@ call as the `:params` key of the options hash map.
|
|||
;;=> ["SELECT * FROM table WHERE a = ?" 42]
|
||||
```
|
||||
|
||||
Or with `:numbered true`:
|
||||
```clojure
|
||||
(sql/format {:select [:*] :from [:table]
|
||||
:where [:= :a :?x]}
|
||||
{:params {:x 42} :numbered true})
|
||||
;;=> ["SELECT * FROM table WHERE a = $1" 42]
|
||||
(sql/format {:select [:*] :from [:table]
|
||||
:where [:= :a [:param :x]]}
|
||||
{:params {:x 42} :numbered true})
|
||||
;;=> ["SELECT * FROM table WHERE a = $1" 42]
|
||||
```
|
||||
|
||||
## Functional Helpers
|
||||
|
||||
In addition to the hash map (and sequences) approach of building
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ All options may be omitted. The default behavior of each option is described in
|
|||
* `:checking` -- `:none` (default), `:basic`, or `:strict` to control the amount of lint-like checking that HoneySQL performs,
|
||||
* `:dialect` -- a keyword that identifies a dialect to be used for this specific call to `format`; the default is to use what was specified in `set-dialect!` or `:ansi` if no other dialect has been set,
|
||||
* `:inline` -- a Boolean indicating whether or not to inline parameter values, rather than use `?` placeholders and a sequence of parameter values; the default is `false` -- values are not inlined,
|
||||
* `:numbered` -- a Boolean indicating whether to generate numbered placeholders in the generated SQL (`$1`, `$2`, etc) or positional placeholders (`?`); the default is `false` (positional placeholders); this option was added in 2.4.next,
|
||||
* `:params` -- a hash map providing values for named parameters, identified by names (keywords or symbols) that start with `?` in the DSL; the default is that any such named parameters will have `nil` values,
|
||||
* `:quoted` -- a Boolean indicating whether or not to quote (strop) SQL entity names (table and column names); the default is `nil` -- alphanumeric SQL entity names are not quoted but (as of 2.3.928) "unusual" SQL entity names are quoted; a `false` value turns off all quoting,
|
||||
* `:quoted-snake` -- a Boolean indicating whether or not quoted and string SQL entity names should have `-` replaced by `_`; the default is `false` -- quoted and string SQL entity names are left exactly as-is,
|
||||
|
|
@ -29,6 +30,7 @@ global defaults of certain options:
|
|||
|
||||
* `:checking` -- can be `:basic` or `:strict`; specify `:none` to reset to the default,
|
||||
* `:inline` -- can be `true` but consider the security issues this causes by not using parameterized SQL statements; specify `false` (or `nil`) to reset to the default,
|
||||
* `:numbered` -- can be `true` or `false`; specify `false` to reset to the default,
|
||||
* `:quoted` -- can be `true` or `false`; specify `nil` to reset to the default; calling `set-dialect!` or providing a `:dialect` option to `format` will override the global default,
|
||||
* `:quoted-snake` -- can be `true`; specify `false` (or `nil`) to reset to the default.
|
||||
|
||||
|
|
@ -96,6 +98,13 @@ was wrapped in `[:inline `..`]`:
|
|||
|
||||
> Note: you can provide additional inline formatting by extending the `InlineValue` protocol from `honey.sql.protocols` to new types.
|
||||
|
||||
## `:numbered`
|
||||
|
||||
By default, HoneySQL generates SQL using positional placeholders (`?`).
|
||||
Specifying `:numbered true` tells HoneySQL to generate SQL using
|
||||
numbered placeholders instead (`$1`, `$2`, etc). This can be set
|
||||
globally using `set-options!`.
|
||||
|
||||
## `:params`
|
||||
|
||||
The `:params` option provides a mapping from named parameters
|
||||
|
|
|
|||
|
|
@ -10,6 +10,12 @@ Everything that the nilenso library provided (in 0.4.112) is implemented
|
|||
directly in HoneySQL 2.x although a few things have a
|
||||
slightly different syntax.
|
||||
|
||||
If you are using HoneySQL with the Node.js PostgreSQL driver, it
|
||||
only accepts numbered placeholders, not positional placeholders,
|
||||
so you will need to specify the `:numbered true` option that was
|
||||
added in 2.4.next. You may find it convenient to set this option
|
||||
globally, via `set-options!`.
|
||||
|
||||
## Code Examples
|
||||
|
||||
The code examples herein assume:
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@
|
|||
(def ^:private default-quoted-snake (atom nil))
|
||||
(def ^:private default-inline (atom nil))
|
||||
(def ^:private default-checking (atom :none))
|
||||
(def ^:private default-numbered (atom false))
|
||||
|
||||
(def ^:private ^:dynamic *dialect* nil)
|
||||
;; nil would be a better default but that makes testing individual
|
||||
|
|
@ -140,6 +141,7 @@
|
|||
(def ^:private ^:dynamic *dsl* nil)
|
||||
;; caching data to detect expressions that cannot be cached:
|
||||
(def ^:private ^:dynamic *caching* nil)
|
||||
(def ^:private ^:dynamic *numbered* nil)
|
||||
|
||||
;; clause helpers
|
||||
|
||||
|
|
@ -321,6 +323,18 @@
|
|||
{::wrapper
|
||||
(fn [fk _] (param-value (fk)))}))
|
||||
|
||||
(defn ->numbered [v]
|
||||
(let [n (count (swap! *numbered* conj v))]
|
||||
[(str "$" n) (with-meta (constantly (dec n))
|
||||
{::wrapper
|
||||
(fn [fk _] (get @*numbered* (fk)))})]))
|
||||
|
||||
(defn ->numbered-param [k]
|
||||
(let [n (count (swap! *numbered* conj k))]
|
||||
[(str "$" n) (with-meta (constantly (dec n))
|
||||
{::wrapper
|
||||
(fn [fk _] (param-value (get @*numbered* (fk))))})]))
|
||||
|
||||
(def ^:private ^:dynamic *formatted-column* (atom false))
|
||||
|
||||
(defn- format-var [x & [opts]]
|
||||
|
|
@ -335,9 +349,12 @@
|
|||
"(" (str/join ", " quoted-args) ")")])
|
||||
(= \? (first c))
|
||||
(let [k (keyword (subs c 1))]
|
||||
(if *inline*
|
||||
[(sqlize-value (param-value k))]
|
||||
["?" (->param k)]))
|
||||
(cond *inline*
|
||||
[(sqlize-value (param-value k))]
|
||||
*numbered*
|
||||
(->numbered-param k)
|
||||
:else
|
||||
["?" (->param k)]))
|
||||
(= \' (first c))
|
||||
(do
|
||||
(reset! *formatted-column* true)
|
||||
|
|
@ -1279,30 +1296,44 @@
|
|||
(defn- format-in [in [x y]]
|
||||
(let [[sql-x & params-x] (format-expr x {:nested true})
|
||||
[sql-y & params-y] (format-expr y {:nested true})
|
||||
values (unwrap (first params-y) {})]
|
||||
[v1 :as values] (map #(unwrap % {}) params-y)]
|
||||
;; #396: prevent caching IN () when named parameter is used:
|
||||
(when (and (meta (first params-y))
|
||||
(::wrapper (meta (first params-y)))
|
||||
*caching*)
|
||||
(throw (ex-info "SQL that includes IN () expressions cannot be cached" {})))
|
||||
(when-not (= :none *checking*)
|
||||
(when (or (and (sequential? y) (empty? y))
|
||||
(and (sequential? values) (empty? values)))
|
||||
(when (or (and (sequential? y) (empty? y))
|
||||
(and (sequential? v1) (empty? v1)))
|
||||
(throw (ex-info "IN () empty collection is illegal"
|
||||
{:clause [in x y]})))
|
||||
(when (and (= :strict *checking*)
|
||||
(or (and (sequential? y) (some nil? y))
|
||||
(and (sequential? values) (some nil? values))))
|
||||
(or (and (sequential? y) (some nil? y))
|
||||
(and (sequential? v1) (some nil? v1))))
|
||||
(throw (ex-info "IN (NULL) does not match"
|
||||
{:clause [in x y]}))))
|
||||
(if (and (= "?" sql-y) (= 1 (count params-y)) (coll? values))
|
||||
(let [sql (str "(" (str/join ", " (repeat (count values) "?")) ")")]
|
||||
(-> [(str sql-x " " (sql-kw in) " " sql)]
|
||||
(into params-x)
|
||||
(into values)))
|
||||
(-> [(str sql-x " " (sql-kw in) " " sql-y)]
|
||||
(into params-x)
|
||||
(into params-y)))))
|
||||
(cond (and (not *numbered*)
|
||||
(= "?" sql-y)
|
||||
(= 1 (count params-y))
|
||||
(coll? v1))
|
||||
(let [sql (str "(" (str/join ", " (repeat (count v1) "?")) ")")]
|
||||
(-> [(str sql-x " " (sql-kw in) " " sql)]
|
||||
(into params-x)
|
||||
(into v1)))
|
||||
(and *numbered*
|
||||
(= (str "$" (count @*numbered*)) sql-y)
|
||||
(= 1 (count params-y))
|
||||
(coll? v1))
|
||||
(let [vs (for [v v1] (->numbered v))
|
||||
sql (str "(" (str/join ", " (map first vs)) ")")]
|
||||
(-> [(str sql-x " " (sql-kw in) " " sql)]
|
||||
(into params-x)
|
||||
(conj nil)
|
||||
(into (map second vs))))
|
||||
:else
|
||||
(-> [(str sql-x " " (sql-kw in) " " sql-y)]
|
||||
(into params-x)
|
||||
(into (if *numbered* values params-y))))))
|
||||
|
||||
(defn- function-0 [k xs]
|
||||
[(str (sql-kw k)
|
||||
|
|
@ -1465,13 +1496,16 @@
|
|||
(into [(str "LATERAL " sql)] params))))
|
||||
:lift
|
||||
(fn [_ [x]]
|
||||
(if *inline*
|
||||
;; this is pretty much always going to be wrong,
|
||||
;; but it could produce a valid result so we just
|
||||
;; assume that the user knows what they are doing:
|
||||
[(sqlize-value x)]
|
||||
["?" (with-meta (constantly x)
|
||||
{::wrapper (fn [fx _] (fx))})]))
|
||||
(cond *inline*
|
||||
;; this is pretty much always going to be wrong,
|
||||
;; but it could produce a valid result so we just
|
||||
;; assume that the user knows what they are doing:
|
||||
[(sqlize-value x)]
|
||||
*numbered*
|
||||
(->numbered x)
|
||||
:else
|
||||
["?" (with-meta (constantly x)
|
||||
{::wrapper (fn [fx _] (fx))})]))
|
||||
:nest
|
||||
(fn [_ [x]]
|
||||
(let [[sql & params] (format-expr x)]
|
||||
|
|
@ -1503,9 +1537,12 @@
|
|||
(into [(str/join ", " sqls)] params)))
|
||||
:param
|
||||
(fn [_ [k]]
|
||||
(if *inline*
|
||||
[(sqlize-value (param-value k))]
|
||||
["?" (->param k)]))
|
||||
(cond *inline*
|
||||
[(sqlize-value (param-value k))]
|
||||
*numbered*
|
||||
(->numbered-param k)
|
||||
:else
|
||||
["?" (->param k)]))
|
||||
:raw
|
||||
(fn [_ [xs]]
|
||||
(raw-render xs))
|
||||
|
|
@ -1586,9 +1623,12 @@
|
|||
["NULL"]
|
||||
|
||||
:else
|
||||
(if *inline*
|
||||
[(sqlize-value expr)]
|
||||
["?" expr])))
|
||||
(cond *inline*
|
||||
[(sqlize-value expr)]
|
||||
*numbered*
|
||||
(->numbered expr)
|
||||
:else
|
||||
["?" expr])))
|
||||
|
||||
(defn- check-dialect [dialect]
|
||||
(when-not (contains? @dialects dialect)
|
||||
|
|
@ -1628,7 +1668,10 @@
|
|||
([data opts]
|
||||
(let [cache (:cache opts)
|
||||
dialect? (contains? opts :dialect)
|
||||
dialect (when dialect? (get @dialects (check-dialect (:dialect opts))))]
|
||||
dialect (when dialect? (get @dialects (check-dialect (:dialect opts))))
|
||||
numbered (if (contains? opts :numbered)
|
||||
(:numbered opts)
|
||||
@default-numbered)]
|
||||
(binding [*dialect* (if dialect? dialect @default-dialect)
|
||||
*caching* cache
|
||||
*checking* (if (contains? opts :checking)
|
||||
|
|
@ -1642,6 +1685,8 @@
|
|||
*inline* (if (contains? opts :inline)
|
||||
(:inline opts)
|
||||
@default-inline)
|
||||
*numbered* (when numbered
|
||||
(atom []))
|
||||
*quoted* (cond (contains? opts :quoted)
|
||||
(:quoted opts)
|
||||
dialect?
|
||||
|
|
@ -1681,11 +1726,12 @@
|
|||
"Set default values for any or all of the following options:
|
||||
* :checking
|
||||
* :inline
|
||||
* :numbered
|
||||
* :quoted
|
||||
* :quoted-snake
|
||||
Note that calling `set-dialect!` can override the default for `:quoted`."
|
||||
[opts]
|
||||
(let [unknowns (dissoc opts :checking :inline :quoted :quoted-snake)]
|
||||
(let [unknowns (dissoc opts :checking :inline :numbered :quoted :quoted-snake)]
|
||||
(when (seq unknowns)
|
||||
(throw (ex-info (str (str/join ", " (keys unknowns))
|
||||
" are not options that can be set globally.")
|
||||
|
|
@ -1693,11 +1739,13 @@
|
|||
(when (contains? opts :checking)
|
||||
(reset! default-checking (:checking opts)))
|
||||
(when (contains? opts :inline)
|
||||
(reset! default-checking (:inline opts)))
|
||||
(reset! default-inline (:inline opts)))
|
||||
(when (contains? opts :numbered)
|
||||
(reset! default-numbered (:numbered opts)))
|
||||
(when (contains? opts :quoted)
|
||||
(reset! default-checking (:quoted opts)))
|
||||
(reset! default-quoted (:quoted opts)))
|
||||
(when (contains? opts :quoted-snake)
|
||||
(reset! default-checking (:quoted-snake opts)))))
|
||||
(reset! default-quoted-snake (:quoted-snake opts)))))
|
||||
|
||||
(defn clause-order
|
||||
"Return the current order that known clauses will be applied when
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
;; copyright (c) 2020-2021 sean corfield, all rights reserved
|
||||
;; copyright (c) 2020-2022 sean corfield, all rights reserved
|
||||
|
||||
(ns honey.sql.helpers-test
|
||||
(:refer-clojure :exclude [filter for group-by partition-by set update])
|
||||
|
|
@ -322,7 +322,17 @@
|
|||
(sql/format {:select [:*]
|
||||
:from [:customers]
|
||||
:where [:in :id :?ids]}
|
||||
{:params {:ids values}})))))))
|
||||
{:params {:ids values}})))
|
||||
(is (= ["SELECT * FROM customers WHERE id IN ($1, $2)" "1" "2"]
|
||||
(sql/format {:select [:*]
|
||||
:from [:customers]
|
||||
:where [:in :id values]}
|
||||
{:numbered true})))
|
||||
(is (= ["SELECT * FROM customers WHERE id IN ($2, $3)" nil "1" "2"]
|
||||
(sql/format {:select [:*]
|
||||
:from [:customers]
|
||||
:where [:in :id :?ids]}
|
||||
{:params {:ids values} :numbered true})))))))
|
||||
|
||||
(deftest test-case
|
||||
(is (= ["SELECT CASE WHEN foo < ? THEN ? WHEN (foo > ?) AND ((foo MOD ?) = ?) THEN foo / ? ELSE ? END FROM bar"
|
||||
|
|
|
|||
|
|
@ -94,6 +94,38 @@
|
|||
(is (= ["SELECT * FROM \"table\" WHERE \"id\" IN (?, ?, ?, ?)" 1 2 3 4]
|
||||
(sut/format {:select [:*] :from [:table] :where [:in :id [1 2 3 4]]} {:quoted true}))))
|
||||
|
||||
(deftest general-numbered-tests
|
||||
(is (= ["SELECT * FROM \"table\" WHERE \"id\" = $1" 1]
|
||||
(sut/format {:select [:*] :from [:table] :where [:= :id 1]}
|
||||
{:quoted true :numbered true})))
|
||||
(is (= ["SELECT * FROM \"table\" WHERE \"id\" = $1" 1]
|
||||
(sut/format {:select [:*] :from [:table] :where (sut/map= {:id 1})}
|
||||
{:quoted true :numbered true})))
|
||||
(is (= ["SELECT \"t\".* FROM \"table\" AS \"t\" WHERE \"id\" = $1" 1]
|
||||
(sut/format {:select [:t.*] :from [[:table :t]] :where [:= :id 1]}
|
||||
{:quoted true :numbered true})))
|
||||
(is (= ["SELECT * FROM \"table\" GROUP BY \"foo\", \"bar\""]
|
||||
(sut/format {:select [:*] :from [:table] :group-by [:foo :bar]}
|
||||
{:quoted true :numbered true})))
|
||||
(is (= ["SELECT * FROM \"table\" GROUP BY DATE(\"bar\")"]
|
||||
(sut/format {:select [:*] :from [:table] :group-by [[:date :bar]]}
|
||||
{:quoted true :numbered true})))
|
||||
(is (= ["SELECT * FROM \"table\" ORDER BY \"foo\" DESC, \"bar\" ASC"]
|
||||
(sut/format {:select [:*] :from [:table] :order-by [[:foo :desc] :bar]}
|
||||
{:quoted true :numbered true})))
|
||||
(is (= ["SELECT * FROM \"table\" ORDER BY DATE(\"expiry\") DESC, \"bar\" ASC"]
|
||||
(sut/format {:select [:*] :from [:table] :order-by [[[:date :expiry] :desc] :bar]}
|
||||
{:quoted true :numbered true})))
|
||||
(is (= ["SELECT * FROM \"table\" WHERE DATE_ADD(\"expiry\", INTERVAL $1 DAYS) < NOW()" 30]
|
||||
(sut/format {:select [:*] :from [:table] :where [:< [:date_add :expiry [:interval 30 :days]] [:now]]}
|
||||
{:quoted true :numbered true})))
|
||||
(is (= ["SELECT * FROM `table` WHERE `id` = $1" 1]
|
||||
(sut/format {:select [:*] :from [:table] :where [:= :id 1]}
|
||||
{:dialect :mysql :numbered true})))
|
||||
(is (= ["SELECT * FROM \"table\" WHERE \"id\" IN ($1, $2, $3, $4)" 1 2 3 4]
|
||||
(sut/format {:select [:*] :from [:table] :where [:in :id [1 2 3 4]]}
|
||||
{:quoted true :numbered true}))))
|
||||
|
||||
;; issue-based tests
|
||||
|
||||
(deftest subquery-alias-263
|
||||
|
|
@ -852,7 +884,13 @@ ORDER BY id = ? DESC
|
|||
{:params {:y [nil]}})))
|
||||
(is (= ["WHERE x IN (?)" nil]
|
||||
(format {:where [:in :x :?y]}
|
||||
{:params {:y [nil]} :checking :basic}))))
|
||||
{:params {:y [nil]} :checking :basic})))
|
||||
(is (= ["WHERE x IN ($2)" nil nil]
|
||||
(format {:where [:in :x :?y]}
|
||||
{:params {:y [nil]} :numbered true})))
|
||||
(is (= ["WHERE x IN ($2)" nil nil]
|
||||
(format {:where [:in :x :?y]}
|
||||
{:params {:y [nil]} :checking :basic :numbered true}))))
|
||||
(testing "IN NULL is flagged in strict mode"
|
||||
(is (thrown-with-msg? ExceptionInfo #"does not match"
|
||||
(format {:where [:in :x [nil]]}
|
||||
|
|
|
|||
Loading…
Reference in a new issue