fix #497 by adding tests and documenting :alias

This commit is contained in:
Sean Corfield 2023-08-26 12:31:38 -07:00
parent 2f99103ed1
commit 686cbf7272
5 changed files with 84 additions and 18 deletions

View file

@ -3,7 +3,7 @@
* 2.4.next in progress * 2.4.next in progress
* Add `:create-or-replace-view` to support PostgreSQL's lack of `IF NOT EXISTS` for `CREATE VIEW`. * Add `:create-or-replace-view` to support PostgreSQL's lack of `IF NOT EXISTS` for `CREATE VIEW`.
* Add `:select` with function call and alias example to README (PR [#502](https://github.com/seancorfield/honeysql/pull/502) [@markbastian](https://github.com/markbastian)). * Add `:select` with function call and alias example to README (PR [#502](https://github.com/seancorfield/honeysql/pull/502) [@markbastian](https://github.com/markbastian)).
* Address [#497](https://github.com/seancorfield/honeysql/issues/497) by adding `:alias` special syntax. Documentation TBD. * Address [#497](https://github.com/seancorfield/honeysql/issues/497) by adding `:alias` special syntax.
* Attempt to clarify the formatting behavior of the `:values` clause when used to produce column names. * Attempt to clarify the formatting behavior of the `:values` clause when used to produce column names.
* Update `tools.build` to 0.9.5 (and remove `:java-opts` setting from `build/run-task`) * Update `tools.build` to 0.9.5 (and remove `:java-opts` setting from `build/run-task`)

View file

@ -490,6 +490,16 @@ Here, `:select` has a three expressions as its argument. The first is
a simple column name. The second is an expression and its alias. The a simple column name. The second is an expression and its alias. The
third is a simple column name and its alias. third is a simple column name and its alias.
An alias can be a simple name (a keyword or a symbol) or a string. An alias
containing a dot (`.`) is treated as a single name for quoting purposes.
Otherwise, a simple name will be formatted using table and column name rules
(including `-` to `_` translation). An alias specified as a string will not get
the `-` to `_` translation. There may be other contexts where you need to
refer to an alias but don't want the table/column rules applied to it, e.g.,
in an `:order-by` clause. You can use the special syntax `[:alias :some.thing]`
to tell HoneySQL to treat `:some.thing` as an alias instead of a table/column
name reference.
`:select-distinct` works the same way but produces `SELECT DISTINCT`. `:select-distinct` works the same way but produces `SELECT DISTINCT`.
> Google BigQuery support: to provide `SELECT * EXCEPT ..` and `SELECT * REPLACE ..` syntax, HoneySQL supports a vector starting with `:*` or the symbol `*` followed by except columns and/or replace expressions as columns: > Google BigQuery support: to provide `SELECT * EXCEPT ..` and `SELECT * REPLACE ..` syntax, HoneySQL supports a vector starting with `:*` or the symbol `*` followed by except columns and/or replace expressions as columns:
@ -967,6 +977,11 @@ user=> (sql/format {:select [:*] :from :table
["SELECT * FROM table ORDER BY CASE WHEN NOW() < expiry_date THEN created_date ELSE expiry_date END DESC"] ["SELECT * FROM table ORDER BY CASE WHEN NOW() < expiry_date THEN created_date ELSE expiry_date END DESC"]
``` ```
You can `ORDER BY` column names (`:col1`), or table and column (`:table.col1`),
or aliases (`:some.alias`). Since there is ambiguity between the formatting
of those, you can use the special syntax `[:alias :some.thing]` to tell
HoneySQL to treat `:some.thing` as an alias instead of a table/column name.
## limit, offset, fetch ## limit, offset, fetch
Some databases, including MySQL, support `:limit` and `:offset` Some databases, including MySQL, support `:limit` and `:offset`

View file

@ -6,6 +6,31 @@ 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` / `:alter-column`). 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` / `:alter-column`).
The examples in this section assume the following:
```clojure
(require '[honey.sql :as sql])
```
## alias
Accepts a single argument which should be an alias name (from an `AS` clause
elsewhere in the overall SQL statement) and uses alias formatting rules rather
than table/column formatting rules (different handling of dots and hyphens).
This allows you to override HoneySQL's default assumption about entity names
and strings.
```clojure
(sql/format {:select [[:column-name "some-alias"]]
:from :b
:order-by [[[:alias "some-alias"]]]})
;;=> ["SELECT column_name AS \"some-alias\" FROM b ORDER BY \"some-alias\" ASC"]
(sql/format {:select [[:column-name :'some-alias]]
:from :b
:order-by [[[:alias :'some-alias]]]})
;;=> ["SELECT column_name AS \"some-alias\" FROM b ORDER BY \"some-alias\" ASC"]
```
## array ## array
Accepts a single argument, which is expected to evaluate to a sequence, Accepts a single argument, which is expected to evaluate to a sequence,
@ -13,8 +38,6 @@ with an optional second argument specifying the type of the array,
and produces `ARRAY[?, ?, ..]` for the elements of that sequence (as SQL parameters): and produces `ARRAY[?, ?, ..]` for the elements of that sequence (as SQL parameters):
```clojure ```clojure
(require '[honey.sql :as sql])
(sql/format-expr [:array (range 5)]) (sql/format-expr [:array (range 5)])
;;=> ["ARRAY[?, ?, ?, ?, ?]" 0 1 2 3 4] ;;=> ["ARRAY[?, ?, ?, ?, ?]" 0 1 2 3 4]
(sql/format-expr [:array (range 3) :text]) (sql/format-expr [:array (range 3) :text])

View file

@ -2108,21 +2108,10 @@
(sql/format {:insert-into :foo :output [:inserted.*] :values [{:bar 1}]}) (sql/format {:insert-into :foo :output [:inserted.*] :values [{:bar 1}]})
(sql/format {:insert-into :foo :columns [:bar] :output [:inserted.*] :values [[1]]}) (sql/format {:insert-into :foo :columns [:bar] :output [:inserted.*] :values [[1]]})
;; issue-497 (sql/format {:select [[:a.b :c.d]]} {:dialect :mysql})
(honey.sql/format {:select [[:column-name "some-alias"]] (sql/format {:select [[:column-name :'some-alias]]
:from :b :from :b
:order-by [[[:raw "\"some-alias\""]]]}) :order-by [[[:alias :'some-alias]]]})
(honey.sql/format {:select [[:column-name "some-alias"]]
:from :b
:order-by [[[:alias "some-alias"]]]})
(honey.sql/format {:select [[:column-name "some-alias"]]
:from :b
:order-by [[[:alias "some-alias"]]]}
{:quoted true})
(honey.sql/format {:select [[:column-name "some-alias"]]
:from :b
:order-by [[[:alias "some-alias"]]]}
{:dialect :mysql})
(sql/format {:select :f.* :from [[:foo [:f :FOR :SYSTEM-TIME]]] :where [:= :f.id 1]}) (sql/format {:select :f.* :from [[:foo [:f :FOR :SYSTEM-TIME]]] :where [:= :f.id 1]})
(sql/format {:using [[:source [:= :table.id :source.id]]]}) (sql/format {:using [[:source [:= :table.id :source.id]]]})
) )

View file

@ -1205,6 +1205,45 @@ ORDER BY id = ? DESC
(is (= ["INNER JOIN (tbl1 LEFT JOIN tbl2 USING (id))"] (is (= ["INNER JOIN (tbl1 LEFT JOIN tbl2 USING (id))"]
(sut/format {:join [[[:join :tbl1 {:left-join [:tbl2 [:using :id]]}]]]}))))) (sut/format {:join [[[:join :tbl1 {:left-join [:tbl2 [:using :id]]}]]]})))))
(deftest issue-497-alias
(is (= ["SELECT column_name AS \"some-alias\" FROM b ORDER BY \"some-alias\" ASC"]
(sut/format {:select [[:column-name "some-alias"]]
:from :b
:order-by [[[:raw "\"some-alias\""]]]})))
;; likely illegal SQL, but shows quoted keyword escaping the -/_ replace:
(is (= ["SELECT column_name AS \"some-alias\" FROM b ORDER BY some-alias ASC"]
(sut/format {:select [[:column-name "some-alias"]]
:from :b
:order-by [[:'some-alias]]})))
(is (= ["SELECT column_name AS \"some-alias\" FROM b ORDER BY \"some-alias\" ASC"]
(sut/format {:select [[:column-name "some-alias"]]
:from :b
:order-by [[[:alias "some-alias"]]]})))
(is (= ["SELECT column_name AS \"some-alias\" FROM b ORDER BY some_alias ASC"]
(sut/format {:select [[:column-name "some-alias"]]
:from :b
:order-by [[[:alias :some-alias]]]})))
(is (= ["SELECT column_name AS \"some-alias\" FROM b ORDER BY \"some-alias\" ASC"]
(sut/format {:select [[:column-name "some-alias"]]
:from :b
:order-by [[[:alias :'some-alias]]]})))
(is (= ["SELECT column_name AS \"some-alias\" FROM b ORDER BY \"some-alias\" ASC"]
(sut/format {:select [[:column-name "some-alias"]]
:from :b
:order-by [[[:alias "some-alias"]]]})))
(is (= ["SELECT \"column-name\" AS \"some-alias\" FROM \"b\" ORDER BY ? ASC"
"some-alias"]
(sut/format {:select [[:column-name "some-alias"]]
:from :b
:order-by ["some-alias"]}
{:quoted true})))
(is (= ["SELECT `column-name` AS `some-alias` FROM `b` ORDER BY `some-alias` ASC"]
(sut/format {:select [[:column-name "some-alias"]]
:from :b
:order-by [[[:alias "some-alias"]]]}
{:dialect :mysql}))))
(comment (comment
;; partial workaround for #407: ;; partial workaround for #407:
(sut/format {:select :f.* :from [[:foo [:f :for :system-time]]] :where [:= :f.id 1]}) (sut/format {:select :f.* :from [[:foo [:f :for :system-time]]] :where [:= :f.id 1]})