add distinct/expr clauses

This commit is contained in:
Sean Corfield 2023-09-08 22:28:25 -07:00
parent 75830df509
commit ac09fc1abd
5 changed files with 42 additions and 11 deletions

View file

@ -1,7 +1,7 @@
# Changes
* 2.4.next in progress
* Address [#504](https://github.com/seancorfield/honeysql/issues/504) by adding special syntax for ignore/respect nulls. More work will be needed to support distinct/order by/limit in BigQuery array aggregation.
* Address [#504](https://github.com/seancorfield/honeysql/issues/504) for BigQuery support, by adding special syntax for ignore/respect nulls, as well as new `:distinct` and `:expr` clauses to allow expressions to be qualified with SQL clauses. The latter will probably be useful for other dialects too.
* 2.4.1066 -- 2023-08-27
* Add `:select` with function call and alias example to README (PR [#502](https://github.com/seancorfield/honeysql/pull/502) [@markbastian](https://github.com/markbastian)).

View file

@ -52,11 +52,11 @@ section of the documentation before trying to use HoneySQL to build your own que
From Clojure:
<!-- {:test-doc-blocks/reader-cond :clj} -->
```clojure
(refer-clojure :exclude '[filter for group-by into partition-by set update])
(refer-clojure :exclude '[distinct filter for group-by into partition-by set update])
(require '[honey.sql :as sql]
;; CAUTION: this overwrites several clojure.core fns:
;;
;; filter, for, group-by, into, partition-by, set, and update
;; distinct, filter, for, group-by, into, partition-by, set, and update
;;
;; you should generally only refer in the specific
;; helpers that you want to use!

View file

@ -1002,6 +1002,19 @@ user=> (sql/format (-> (select :id
["SELECT id, AVG(salary) OVER () AS Average, MAX(salary) OVER () AS MaxSalary FROM employee"]
```
## distinct, expr
Related to the windowing clauses above, `:distinct` and `:expr` are
intended to let you mix clauses with expressions, such as in BigQuery's
`ARRAY_AGG` function:
```clojure
user=> (sql/format {:select [[[:over
[[:array_agg {:distinct [:ignore-nulls :col] :order-by :x}]
{:partition-by :y}]]]]})
["SELECT ARRAY_AGG (DISTINCT col IGNORE NULLS ORDER BY x ASC) OVER (PARTITION BY y)"]
```
## order-by
`:order-by` accepts a sequence of one or more ordering

View file

@ -55,6 +55,7 @@
:raw :nest :with :with-recursive :intersect :union :union-all :except :except-all
:table
:select :select-distinct :select-distinct-on :select-top :select-distinct-top
:distinct :expr
:into :bulk-collect-into
:insert-into :replace-into :update :delete :delete-from :truncate
:columns :set :from :using
@ -288,13 +289,14 @@
Any ? is escaped to ??."
[k]
(when k
(let [n (str/replace (name k) "?" "??")]
(if (= \' (first n))
(let [ident (subs n 1 (count n))
ident-l (str/lower-case ident)]
(binding [*quoted* (when-not (contains? #{"array"} ident-l) *quoted*)]
(format-entity (keyword ident))))
(-> n (dehyphen) (upper-case)))))
(-> n (dehyphen) (upper-case))))))
(defn- sym->kw
"Given a symbol, produce a keyword, retaining the namespace
@ -1361,6 +1363,8 @@
:select-distinct-on #'format-selects-on
:select-top #'format-select-top
:select-distinct-top #'format-select-top
:distinct (fn [k xs] (format-selects k [[xs]]))
:expr (fn [_ xs] (format-selects nil [[xs]]))
:into #'format-select-into
:bulk-collect-into #'format-select-into
:insert-into #'format-insert
@ -2269,4 +2273,8 @@
[:transport :t] [:id :name]]
:values [[1 "Car"] [2 "Boat"] [3 "Bike"]]}
{:pretty true})
(sql/format-expr [:array_agg {:distinct [:ignore-nulls :a] :order-by :a}])
(sql/format-expr [:over [[:array_agg {:expr [:respect-nulls :a] :order-by :a
:limit 10}]
{:partition-by :something}]])
)

View file

@ -48,7 +48,7 @@
bulk-collect-info [& args]
(as they are for all helper functions)."
(:refer-clojure :exclude [filter for group-by into partition-by set update])
(:refer-clojure :exclude [distinct filter for group-by into partition-by set update])
(:require [clojure.core :as c]
[honey.sql]))
@ -468,6 +468,16 @@
[& args]
(generic :select-distinct-top args))
(defn distinct
"Like `select-distinct` but produces DISTINCT..."
[& args]
(generic-1 :distinct args))
(defn expr
"Like `distinct` but produces ... (i.e., just the expression that follows)."
[& args]
(generic-1 :expr args))
(defn into
"Accepts table name, optionally followed a database name."
{:arglists '([table] [table dbname])}