diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/clojure_sql/dsl.clj | 30 |
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. |