summaryrefslogtreecommitdiff
path: root/src/clojure_sql
diff options
context:
space:
mode:
Diffstat (limited to 'src/clojure_sql')
-rw-r--r--src/clojure_sql/dsl.clj30
1 files changed, 25 insertions, 5 deletions
diff --git a/src/clojure_sql/dsl.clj b/src/clojure_sql/dsl.clj
index 16bc2e5..413b9c0 100644
--- a/src/clojure_sql/dsl.clj
+++ b/src/clojure_sql/dsl.clj
@@ -61,6 +61,10 @@
:right right
:type type })))
+(defn ^:private not-a-function-expression [expr]
+ (throw (ex-info "Expr is not a function application - could not possibly be an aggregate"
+ {:expr expr})))
+
(defn ^:private invalid-union [queries]
(throw (ex-info "Cannot union queries with different fields"
{:queries queries})))
@@ -326,19 +330,35 @@
(defn group
"Apply a grouping to a query.
- `fields` is a sequential collection of fields to group by.
+ `fields` is a collection of fields to group by. `projection` is a
+ map with which to perform a projection.
+
+ The projection must perform an aggregate function on each
+ non-grouped field, or else the resulting group is invalid. This is
+ not verifiable without knowing the full set of aggregate
+ functions (which is not possible in general), so any projection will
+ be accepted as long as a function is called as a part of each
+ projection.
If the query has already been grouped then this will create a
subquery."
- [query fields]
+ [query fields projection]
(let [query (if (:group query)
(convert-to-subquery query)
query)
fields-seq (if (sequential? fields)
fields
- [fields])]
- (assoc query :group (map (partial resolve-field (:tables query) (:fields query))
- fields-seq))))
+ [fields])
+ fields-map (into {} (map vector fields fields))]
+ (doseq [[expr name] projection]
+ (try (or (some #{expr} fields-seq)
+ (seq expr))
+ (catch Exception e
+ (not-a-function-expression expr))))
+ (-> query
+ (assoc :group (map (partial resolve-field (:tables query) (:fields query))
+ fields-seq))
+ (project (into fields-map projection)))))
(defn take
"Limit the number of results of a query.