Fix #382 by adding :case-expr syntax

This commit is contained in:
Sean Corfield 2022-01-29 17:19:17 -08:00
parent 139de6f56c
commit 8a2f447676
4 changed files with 48 additions and 19 deletions

View file

@ -1,7 +1,8 @@
# Changes # Changes
* 2.2.next in progress * 2.2.next in progress
* Fix #380 by correcting test for function type in `register-clause!`. * Address [#382](https://github.com/seancorfield/honeysql/issues/382) by adding `:case-expr` for BigQuery support.
* Fix [#380](https://github.com/seancorfield/honeysql/issues/380) by correcting test for function type in `register-clause!` and `register-fn!`.
* 2.2.858 -- 2022-01-20 * 2.2.858 -- 2022-01-20
* Address #377 by adding `honey.sql/map=` to convert a hash map into an equality condition (for a `WHERE` clause). * Address #377 by adding `honey.sql/map=` to convert a hash map into an equality condition (for a `WHERE` clause).

View file

@ -41,6 +41,15 @@ may be `:else` (or `'else`) to produce `ELSE`, otherwise
;; => ["CASE WHEN a < ? THEN ? WHEN a > ? THEN ? ELSE ? END" 10 "small" 100 "big" "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`:
```clojure
(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"]
```
## cast ## cast
A SQL CAST expression. Expects an expression and something A SQL CAST expression. Expects an expression and something

View file

@ -1124,6 +1124,31 @@
(partition 2 pairs))] (partition 2 pairs))]
(into [(str/join ", " sqls)] params))) (into [(str/join ", " sqls)] params)))
(defn- case-clauses
"For both :case and :case-expr."
[k clauses]
(let [case-expr? (= :case-expr k)
[sqlx & paramsx] (when case-expr? (format-expr (first clauses)))
[sqls params]
(reduce (fn [[sqls params] [condition value]]
(let [[sqlc & paramsc] (when-not (= :else condition)
(format-expr condition))
[sqlv & paramsv] (format-expr value)]
[(if (or (= :else condition)
(= 'else condition))
(conj sqls (sql-kw :else) sqlv)
(conj sqls (sql-kw :when) sqlc (sql-kw :then) sqlv))
(-> params (into paramsc) (into paramsv))]))
[[] []]
(partition 2 (if case-expr? (rest clauses) clauses)))]
(-> [(str (sql-kw :case) " "
(when case-expr?
(str sqlx " "))
(str/join " " sqls)
" " (sql-kw :end))]
(into paramsx)
(into params))))
(def ^:private special-syntax (def ^:private special-syntax
(atom (atom
{;; these "functions" are mostly used in column {;; these "functions" are mostly used in column
@ -1168,24 +1193,8 @@
(into params-x) (into params-x)
(into params-a) (into params-a)
(into params-b)))) (into params-b))))
:case :case #'case-clauses
(fn [_ clauses] :case-expr #'case-clauses
(let [[sqls params]
(reduce (fn [[sqls params] [condition value]]
(let [[sqlc & paramsc] (when-not (= :else condition)
(format-expr condition))
[sqlv & paramsv] (format-expr value)]
[(if (or (= :else condition)
(= 'else condition))
(conj sqls (sql-kw :else) sqlv)
(conj sqls (sql-kw :when) sqlc (sql-kw :then) sqlv))
(-> params (into paramsc) (into paramsv))]))
[[] []]
(partition 2 clauses))]
(into [(str (sql-kw :case) " "
(str/join " " sqls)
" " (sql-kw :end))]
params)))
:cast :cast
(fn [_ [x type]] (fn [_ [x type]]
(let [[sql & params] (format-expr x) (let [[sql & params] (format-expr x)

View file

@ -42,3 +42,13 @@
{:add-column [:name :string :if-not-exists]} {:add-column [:name :string :if-not-exists]}
{:add-column [:my_struct [:bigquery/struct [:name :string] [:description :string]] :if-not-exists]} {:add-column [:my_struct [:bigquery/struct [:name :string] [:description :string]] :if-not-exists]}
{:add-column [:my_array [:bigquery/array :string] :if-not-exists]}]})))) {:add-column [:my_array [:bigquery/array :string] :if-not-exists]}]}))))
(deftest test-case-expr
(is (= ["SELECT CASE foo WHEN ? THEN ? WHEN ? THEN foo / ? ELSE ? END FROM bar"
1 -1 2 2 0]
(sut/format
{:select [[[:case-expr :foo
1 -1
2 [:/ :foo 2]
:else 0]]]
:from [:bar]}))))