diff --git a/src/metabase/driver.clj b/src/metabase/driver.clj
index adc204a0be246495cfec6288fd311b5ed23e15c7..c2e2a1a2887dca4ef1bd1d596f9743bfd521f877 100644
--- a/src/metabase/driver.clj
+++ b/src/metabase/driver.clj
@@ -135,7 +135,8 @@
   *  `:standard-deviation-aggregations` - Does this driver support [standard deviation aggregations](https://github.com/metabase/metabase/wiki/Query-Language-'98#stddev-aggregation)?
   *  `:expressions` - Does this driver support [expressions](https://github.com/metabase/metabase/wiki/Query-Language-'98#expressions) (e.g. adding the values of 2 columns together)?
   *  `:dynamic-schema` -  Does this Database have no fixed definitions of schemas? (e.g. Mongo)
-  *  `:native-parameters` - Does the driver support parameter substitution on native queries?")
+  *  `:native-parameters` - Does the driver support parameter substitution on native queries?
+  *  `:expression-aggregations` - Does the driver support using expressions inside aggregations? e.g. something like \"sum(x) + count(y)\" or \"avg(x + y)\"")
 
   (field-values-lazy-seq ^clojure.lang.Sequential [this, ^FieldInstance field]
     "Return a lazy sequence of all values of FIELD.
diff --git a/src/metabase/driver/druid.clj b/src/metabase/driver/druid.clj
index 5ad989640e911e78139aeec0f8470400988f8e37..6f652e66cf5b1a5e6bcb642b4b3c152275923c38 100644
--- a/src/metabase/driver/druid.clj
+++ b/src/metabase/driver/druid.clj
@@ -158,7 +158,7 @@
                                                :type         :integer
                                                :default      8082}])
           :execute-query         (fn [_ query] (qp/execute-query do-query query))
-          :features              (constantly #{:basic-aggregations :set-timezone})
+          :features              (constantly #{:basic-aggregations :set-timezone :expression-aggregations})
           :field-values-lazy-seq (u/drop-first-arg field-values-lazy-seq)
           :mbql->native          (u/drop-first-arg qp/mbql->native)}))
 
diff --git a/src/metabase/driver/druid/js.clj b/src/metabase/driver/druid/js.clj
new file mode 100644
index 0000000000000000000000000000000000000000..5b55efc48b39e6125aaaa847fbb78d4ad4dffb7e
--- /dev/null
+++ b/src/metabase/driver/druid/js.clj
@@ -0,0 +1,68 @@
+(ns metabase.driver.druid.js
+  "Util fns for creating Javascript functions."
+  (:refer-clojure :exclude [+ - * / or])
+  (:require [clojure.string :as s]))
+
+(defn- ->js [x]
+  {:pre [(not (coll? x))]}
+  (if (keyword? x)
+    (name x)
+    x))
+
+(defn parens
+  "Wrap S (presumably a string) in parens."
+  ^String [s]
+  (str "(" s ")"))
+
+(defn argslist
+  "Generate a list of arguments, e.g. for a function declaration or function call.
+   ARGS are separated by commas and wrapped in parens.
+
+     (argslist [:x :y]) -> \"(x, y)\""
+  ^String [args]
+  (parens (s/join ", " (map ->js args))))
+
+(defn return
+  "Generate a javascript `return` statement. STATEMENT-PARTS are combined directly into a single string.
+
+     (return :x :+ :y) -> \"return x+y;\""
+  ^String [& statement-parts]
+  (str "return " (apply str (map ->js statement-parts)) ";"))
+
+(defn function
+  "Create a JavaScript function with ARGS and BODY."
+  {:style/indent 1}
+  ^String [args & body]
+  (str "function" (argslist args) " { "
+       (apply str body)
+       " }"))
+
+(defn- arithmetic-operator
+  "Interpose artihmetic OPERATOR between ARGS, and wrap the entire expression in parens."
+  ^String [operator & args]
+  (parens (s/join (str " " (name operator) " ")
+                  (map ->js args))))
+
+(def ^{:arglists '([& args])} ^String + "Interpose `+` between ARGS, and wrap the entire expression in parens." (partial arithmetic-operator :+))
+(def ^{:arglists '([& args])} ^String - "Interpose `-` between ARGS, and wrap the entire expression in parens." (partial arithmetic-operator :-))
+(def ^{:arglists '([& args])} ^String * "Interpose `*` between ARGS, and wrap the entire expression in parens." (partial arithmetic-operator :*))
+(def ^{:arglists '([& args])} ^String / "Interpose `/` between ARGS, and wrap the entire expression in parens." (partial arithmetic-operator :/))
+
+(defn fn-call
+  "Generate a JavaScript function call.
+
+     (fn-call :parseFloat :x :y) -> \"parseFloat(x, y)\""
+  ^String [fn-name & args]
+  (str (name fn-name) (argslist args)))
+
+(def ^{:arglists '([x])} ^String parse-float
+  "Generate a call to the JavaScript `parseFloat` function."
+  (partial fn-call :parseFloat))
+
+
+(defn or
+  "Interpose the JavaScript or operator (`||`) betwen ARGS, and wrap the entire expression in parens.
+
+     (or :x :y) -> \"(x || y)\""
+  ^String [& args]
+  (parens (s/join " || " (map ->js args))))
diff --git a/src/metabase/driver/druid/query_processor.clj b/src/metabase/driver/druid/query_processor.clj
index 2ad096d4aa2eeb5f423fd67d3f01da4eeda28678..abcbc0fbd723d2d0766f5677c01155fe542a3a0d 100644
--- a/src/metabase/driver/druid/query_processor.clj
+++ b/src/metabase/driver/druid/query_processor.clj
@@ -4,13 +4,16 @@
             [clojure.string :as s]
             [clojure.tools.logging :as log]
             [cheshire.core :as json]
+            [metabase.driver.druid.js :as js]
             [metabase.query-processor :as qp]
-            [metabase.query-processor.interface :as i]
+            (metabase.query-processor [annotate :as annotate]
+                                      [interface :as i])
             [metabase.util :as u])
   (:import clojure.lang.Keyword
            (metabase.query_processor.interface AgFieldRef
                                                DateTimeField
                                                DateTimeValue
+                                               Expression
                                                Field
                                                RelativeDateTimeValue
                                                Value)))
@@ -100,42 +103,112 @@
 
 (declare filter:not filter:nil?)
 
+(defn- field? [arg]
+  (or (instance? Field arg)
+      (instance? DateTimeField arg)))
+
+(defn- expression->field-names [{:keys [args]}]
+  {:post [(every? u/string-or-keyword? %)]}
+  (flatten (for [arg   args
+                 :when (or (field? arg)
+                           (instance? Expression arg))]
+             (cond
+               (instance? Expression arg) (expression->field-names arg)
+               (field? arg)               (->rvalue arg)))))
+
+(defn- expression-arg->js [arg default-value]
+  (if-not (field? arg)
+    arg
+    (js/or (js/parse-float (->rvalue arg))
+           default-value)))
+
+(defn- expression->js [{:keys [operator args]} default-value]
+  (apply (case operator
+           :+ js/+
+           :- js/-
+           :* js/*
+           :/ js//)
+         (for [arg args]
+           (expression-arg->js arg default-value))))
+
+(defn- ag:doubleSum:expression [{operator :operator,  :as expression} output-name]
+  (let [field-names (expression->field-names expression)]
+    {:type        :javascript
+     :name        output-name
+     :fieldNames  field-names
+     :fnReset     (js/function []
+                    (js/return 0))
+     :fnAggregate (js/function (cons :current field-names)
+                    (js/return (js/+ :current (expression->js expression (if (= operator :/) 1 0)))))
+     :fnCombine   (js/function [:x :y]
+                    (js/return (js/+ :x :y)))}))
+
 (defn- ag:doubleSum [field output-name]
-  ;; metrics can use the built-in :doubleSum aggregator, but for dimensions we have to roll something that does the same thing in JS
-  (case (dimension-or-metric? field)
-    :metric    {:type      :doubleSum
-                :name      output-name
-                :fieldName (->rvalue field)}
-    :dimension {:type        :javascript
-                :name        output-name
-                :fieldNames  [(->rvalue field)]
-                :fnReset     "function() { return 0 ; }"
-                :fnAggregate "function(current, x) { return current + (parseFloat(x) || 0); }"
-                :fnCombine   "function(x, y) { return x + y; }"}))
-
-(defn- ag:doubleMin [field]
-  (case (dimension-or-metric? field)
-    :metric    {:type      :doubleMin
-                :name      :min
-                :fieldName (->rvalue field)}
-    :dimension {:type        :javascript
-                :name        :min
-                :fieldNames  [(->rvalue field)]
-                :fnReset     "function() { return Number.MAX_VALUE ; }"
-                :fnAggregate "function(current, x) { return Math.min(current, (parseFloat(x) || Number.MAX_VALUE)); }"
-                :fnCombine   "function(x, y) { return Math.min(x, y); }"}))
-
-(defn- ag:doubleMax [field]
-  (case (dimension-or-metric? field)
-    :metric    {:type      :doubleMax
-                :name      :max
-                :fieldName (->rvalue field)}
-    :dimension {:type        :javascript
-                :name        :max
-                :fieldNames  [(->rvalue field)]
-                :fnReset     "function() { return Number.MIN_VALUE ; }"
-                :fnAggregate "function(current, x) { return Math.max(current, (parseFloat(x) || Number.MIN_VALUE)); }"
-                :fnCombine   "function(x, y) { return Math.max(x, y); }"}))
+  (if (instance? Expression field)
+    (ag:doubleSum:expression field output-name)
+    ;; metrics can use the built-in :doubleSum aggregator, but for dimensions we have to roll something that does the same thing in JS
+    (case (dimension-or-metric? field)
+      :metric    {:type      :doubleSum
+                  :name      output-name
+                  :fieldName (->rvalue field)}
+      :dimension {:type        :javascript
+                  :name        output-name
+                  :fieldNames  [(->rvalue field)]
+                  :fnReset     "function() { return 0 ; }"
+                  :fnAggregate "function(current, x) { return current + (parseFloat(x) || 0); }"
+                  :fnCombine   "function(x, y) { return x + y; }"})))
+
+(defn- ag:doubleMin:expression [expression output-name]
+  (let [field-names (expression->field-names expression)]
+    {:type        :javascript
+     :name        output-name
+     :fieldNames  field-names
+     :fnReset     (js/function []
+                    (js/return "Number.MAX_VALUE"))
+     :fnAggregate (js/function (cons :current field-names)
+                    (js/return (js/fn-call :Math.min :current (expression->js expression :Number.MAX_VALUE))))
+     :fnCombine   (js/function [:x :y]
+                    (js/return (js/fn-call :Math.min :x :y)))}))
+
+(defn- ag:doubleMin [field output-name]
+  (if (instance? Expression field)
+    (ag:doubleMin:expression field output-name)
+    (case (dimension-or-metric? field)
+      :metric    {:type      :doubleMin
+                  :name      output-name
+                  :fieldName (->rvalue field)}
+      :dimension {:type        :javascript
+                  :name        output-name
+                  :fieldNames  [(->rvalue field)]
+                  :fnReset     "function() { return Number.MAX_VALUE ; }"
+                  :fnAggregate "function(current, x) { return Math.min(current, (parseFloat(x) || Number.MAX_VALUE)); }"
+                  :fnCombine   "function(x, y) { return Math.min(x, y); }"})))
+
+(defn- ag:doubleMax:expression [expression output-name]
+  (let [field-names (expression->field-names expression)]
+    {:type        :javascript
+     :name        output-name
+     :fieldNames  field-names
+     :fnReset     (js/function []
+                    (js/return "Number.MIN_VALUE"))
+     :fnAggregate (js/function (cons :current field-names)
+                    (js/return (js/fn-call :Math.max :current (expression->js expression :Number.MIN_VALUE))))
+     :fnCombine   (js/function [:x :y]
+                    (js/return (js/fn-call :Math.max :x :y)))}))
+
+(defn- ag:doubleMax [field output-name]
+  (if (instance? Expression field)
+    (ag:doubleMax:expression field output-name)
+    (case (dimension-or-metric? field)
+      :metric    {:type      :doubleMax
+                  :name      output-name
+                  :fieldName (->rvalue field)}
+      :dimension {:type        :javascript
+                  :name        output-name
+                  :fieldNames  [(->rvalue field)]
+                  :fnReset     "function() { return Number.MIN_VALUE ; }"
+                  :fnAggregate "function(current, x) { return Math.max(current, (parseFloat(x) || Number.MIN_VALUE)); }"
+                  :fnCombine   "function(x, y) { return Math.max(x, y); }"})))
 
 (defn- ag:filtered  [filtr aggregator] {:type :filtered, :filter filtr, :aggregator aggregator})
 
@@ -144,40 +217,82 @@
   ([field output-name] (ag:filtered (filter:not (filter:nil? field))
                                     (ag:count output-name))))
 
-(defn- handle-aggregation [query-type {ag-type :aggregation-type, ag-field :field} druid-query]
+
+(defn- handle-aggregation [query-type {ag-type :aggregation-type, ag-field :field, output-name :output-name, :as ag} druid-query]
   (when (isa? query-type ::ag-query)
     (merge-with concat
       druid-query
       (let [ag-type (when-not (= ag-type :rows) ag-type)]
         (match [ag-type ag-field]
           ;; For 'distinct values' queries (queries with a breakout by no aggregation) just aggregate by count, but name it :___count so it gets discarded automatically
-          [nil     nil] {:aggregations [(ag:count :___count)]}
-
-          [:count  nil] {:aggregations [(ag:count :count)]}
+          [nil     nil] {:aggregations [(ag:count (or output-name :___count))]}
 
-          [:count    _] {:aggregations [(ag:count ag-field :count)]}
+          [:count  nil] {:aggregations [(ag:count (or output-name :count))]}
 
-          [:avg      _] {:aggregations     [(ag:count ag-field :___count)
-                                            (ag:doubleSum ag-field :___sum)]
-                         :postAggregations [{:type   :arithmetic
-                                             :name   :avg
-                                             :fn     :/
-                                             :fields [{:type :fieldAccess, :fieldName :___sum}
-                                                      {:type :fieldAccess, :fieldName :___count}]}]}
+          [:count    _] {:aggregations [(ag:count ag-field (or output-name :count))]}
 
+          [:avg      _] (let [count-name (name (gensym "___count_"))
+                              sum-name   (name (gensym "___sum_"))]
+                          {:aggregations     [(ag:count ag-field count-name)
+                                              (ag:doubleSum ag-field sum-name)]
+                           :postAggregations [{:type   :arithmetic
+                                               :name   (or output-name :avg)
+                                               :fn     :/
+                                               :fields [{:type :fieldAccess, :fieldName sum-name}
+                                                        {:type :fieldAccess, :fieldName count-name}]}]})
           [:distinct _] {:aggregations [{:type       :cardinality
-                                         :name       :distinct___count
+                                         :name       (or output-name :distinct___count)
                                          :fieldNames [(->rvalue ag-field)]}]}
-          [:sum      _] {:aggregations [(ag:doubleSum ag-field :sum)]}
-          [:min      _] {:aggregations [(ag:doubleMin ag-field)]}
-          [:max      _] {:aggregations [(ag:doubleMax ag-field)]})))))
+          [:sum      _] {:aggregations [(ag:doubleSum ag-field (or output-name :sum))]}
+          [:min      _] {:aggregations [(ag:doubleMin ag-field (or output-name :min))]}
+          [:max      _] {:aggregations [(ag:doubleMax ag-field (or output-name :max))]})))))
+
+(defn- add-expression-aggregation-output-names [args]
+  (for [arg args]
+    (cond
+      (number? arg)              arg
+      (:aggregation-type arg)    (assoc arg :output-name (or (:output-name arg)
+                                                             (name (gensym (str "___" (name (:aggregation-type arg)) "_")))))
+      (instance? Expression arg) (update arg :args add-expression-aggregation-output-names))))
+
+(defn- expression-post-aggregation [{:keys [operator args], :as expression}]
+  {:type   :arithmetic
+   :name   (annotate/expression-aggregation-name expression)
+   :fn     operator
+   :fields (for [arg args]
+             (cond
+               (number? arg)              {:type :constant, :name (str arg), :value arg}
+               (:output-name arg)         {:type :fieldAccess, :fieldName (:output-name arg)}
+               (instance? Expression arg) (expression-post-aggregation arg)))})
+
+(declare handle-aggregations)
+
+(defn- expression->actual-ags
+  "Return a flattened list of actual aggregations that are needed for EXPRESSION."
+  [expression]
+  (apply concat (for [arg   (:args expression)
+                      :when (not (number? arg))]
+                  (if (instance? Expression arg)
+                    (expression->actual-ags arg)
+                    [arg]))))
+
+(defn- handle-expression-aggregation [query-type {:keys [operator args], :as expression} druid-query]
+  ;; filter out constants from the args list
+  (let [expression  (update expression :args add-expression-aggregation-output-names)
+        ags         (expression->actual-ags expression)
+        druid-query (handle-aggregations query-type {:aggregation ags} druid-query)]
+    (merge-with concat
+      druid-query
+      {:postAggregations [(expression-post-aggregation expression)]})))
 
 (defn- handle-aggregations [query-type {aggregations :aggregation} druid-query]
   (loop [[ag & more] aggregations, query druid-query]
-    (let [query (handle-aggregation query-type ag query)]
-      (if-not (seq more)
-        query
-        (recur more query)))))
+    (if (instance? Expression ag)
+      (handle-expression-aggregation query-type ag druid-query)
+      (let [query (handle-aggregation query-type ag query)]
+        (if-not (seq more)
+          query
+          (recur more query))))))
 
 
 ;;; ### handle-breakout
@@ -296,6 +411,10 @@
 
 ;;; ### handle-filter
 
+(defn- filter:and [filters]
+  {:type   :and
+   :fields filters})
+
 (defn- filter:not [filtr]
   {:pre [filtr]}
   (if (= (:type filtr) :not)     ; it looks like "two nots don't make an identity" with druid
@@ -308,9 +427,13 @@
    :value     value})
 
 (defn- filter:nil? [field]
-  (filter:= field (case (dimension-or-metric? field)
-                    :dimension nil
-                    :metric    0)))
+  (if (instance? Expression field)
+    (filter:and (for [arg   (:args field)
+                      :when (field? arg)]
+                  (filter:nil? arg)))
+    (filter:= field (case (dimension-or-metric? field)
+                      :dimension nil
+                      :metric    0))))
 
 (defn- filter:js [field fn-format-str & args]
   {:pre [field (string? fn-format-str)]}
diff --git a/src/metabase/query_processor/annotate.clj b/src/metabase/query_processor/annotate.clj
index 07aeae483e65ba6913c819a0733d13dcf4b68f36..22408841e21e95691c6176832c348527e94d1cb0 100644
--- a/src/metabase/query_processor/annotate.clj
+++ b/src/metabase/query_processor/annotate.clj
@@ -7,7 +7,8 @@
             [metabase.models.field :refer [Field]]
             [metabase.query-processor.interface :as i]
             [metabase.util :as u])
-  (:import metabase.query_processor.interface.ExpressionRef))
+  (:import (metabase.query_processor.interface Expression
+                                               ExpressionRef)))
 
 ;; Fields should be returned in the following order:
 ;; 1.  Breakout Fields
@@ -93,29 +94,57 @@
     :count
     ag-type))
 
+(defn expression-aggregation-name
+  "Return an appropriate name for an expression aggregation, e.g. `sum + count`."
+  ^String [ag]
+  (cond
+    ;;
+    (instance? Expression ag) (let [{:keys [operator args]} ag]
+                                (str/join (str " " (name operator) " ")
+                                          (for [arg args]
+                                            (if (instance? Expression arg)
+                                              (str "(" (expression-aggregation-name arg) ")")
+                                              (expression-aggregation-name arg)))))
+    (:aggregation-type ag)    (name (:aggregation-type ag))
+    ;; a constant like
+    :else                     ag))
+
+(defn- expression-aggregate-field-info [expression]
+  (let [ag-name (expression-aggregation-name expression)]
+    {:source             :aggregation
+     :field-name         ag-name
+     :field-display-name ag-name
+     :base-type          :type/Number
+     :special-type       :type/Number}))
+
+(defn- aggregate-field-info [{ag-type :aggregation-type, ag-field :field}]
+  (merge (let [field-name (ag-type->field-name ag-type)]
+           {:source             :aggregation
+            :field-name         field-name
+            :field-display-name field-name
+            :base-type          (:base-type ag-field)
+            :special-type       (:special-type ag-field)})
+         ;; Always treat count or distinct count as an integer even if the DB in question returns it as something wacky like a BigDecimal or Float
+         (when (contains? #{:count :distinct} ag-type)
+           {:base-type    :type/Integer
+            :special-type :type/Number})
+         ;; For the time being every Expression is an arithmetic operator and returns a floating-point number, so hardcoding these types is fine;
+         ;; In the future when we extend Expressions to handle more functionality we'll want to introduce logic that associates a return type with a given expression.
+         ;; But this will work for the purposes of a patch release.
+         (when (instance? ExpressionRef ag-field)
+           {:base-type    :type/Float
+            :special-type :type/Number})))
+
 (defn- add-aggregate-field-if-needed
   "Add a Field containing information about an aggregate column such as `:count` or `:distinct` if needed."
   [{aggregations :aggregation} fields]
   (if (or (empty? aggregations)
           (= (:aggregation-type (first aggregations)) :rows))
     fields
-    (concat fields (for [{ag-type :aggregation-type, ag-field :field} aggregations]
-                     (merge (let [field-name (ag-type->field-name ag-type)]
-                              {:source             :aggregation
-                               :field-name         field-name
-                               :field-display-name field-name
-                               :base-type          (:base-type ag-field)
-                               :special-type       (:special-type ag-field)})
-                            ;; Always treat count or distinct count as an integer even if the DB in question returns it as something wacky like a BigDecimal or Float
-                            (when (contains? #{:count :distinct} ag-type)
-                              {:base-type    :type/Integer
-                               :special-type :type/Number})
-                            ;; For the time being every Expression is an arithmetic operator and returns a floating-point number, so hardcoding these types is fine;
-                            ;; In the future when we extend Expressions to handle more functionality we'll want to introduce logic that associates a return type with a given expression.
-                            ;; But this will work for the purposes of a patch release.
-                            (when (instance? ExpressionRef ag-field)
-                              {:base-type    :type/Float
-                               :special-type :type/Number}))))))
+    (concat fields (for [ag aggregations]
+                     (if (instance? Expression ag)
+                       (expression-aggregate-field-info ag)
+                       (aggregate-field-info ag))))))
 
 (defn- add-unknown-fields-if-needed
   "When create info maps for any fields we didn't expect to come back from the query.
diff --git a/src/metabase/query_processor/expand.clj b/src/metabase/query_processor/expand.clj
index 49920681f7cf90e319a45bedfd7037dbcf13861d..2ad8ad88d633ad97846c05f86e9e34454432da32 100644
--- a/src/metabase/query_processor/expand.clj
+++ b/src/metabase/query_processor/expand.clj
@@ -16,9 +16,9 @@
                                                ComparisonFilter
                                                CompoundFilter
                                                EqualityFilter
+                                               Expression
                                                ExpressionRef
                                                FieldPlaceholder
-                                               Expression
                                                NotFilter
                                                RelativeDatetime
                                                StringFilter
@@ -114,8 +114,13 @@
 
 ;;; ## aggregation
 
+(defn- field-or-expression [f]
+  (if (instance? Expression f)
+    (update f :args (partial map field-or-expression)) ; recursively call field-or-expression on all the args of the expression
+    (field f)))
+
 (s/defn ^:private ^:always-validate ag-with-field :- i/Aggregation [ag-type f]
-  (i/strict-map->AggregationWithField {:aggregation-type ag-type, :field (field f)}))
+  (i/strict-map->AggregationWithField {:aggregation-type ag-type, :field (field-or-expression f)}))
 
 (def ^:ql ^{:arglists '([f])} avg      "Aggregation clause. Return the average value of F."                (partial ag-with-field :avg))
 (def ^:ql ^{:arglists '([f])} distinct "Aggregation clause. Return the number of distinct values of F."    (partial ag-with-field :distinct))
@@ -162,9 +167,11 @@
      (map? ag-or-ags)  (recur query [ag-or-ags])
      (empty? ag-or-ags) query
      :else              (assoc query :aggregation (vec (for [ag ag-or-ags]
-                                                         (u/prog1 ((if (:field ag)
-                                                                     i/map->AggregationWithField
-                                                                     i/map->AggregationWithoutField) (update ag :aggregation-type normalize-token))
+                                                         ;; make sure the ag map is still typed correctly
+                                                         (u/prog1 (cond
+                                                                    (:operator ag) (i/map->Expression ag)
+                                                                    (:field ag)    (i/map->AggregationWithField    (update ag :aggregation-type normalize-token))
+                                                                    :else          (i/map->AggregationWithoutField (update ag :aggregation-type normalize-token)))
                                                            (s/validate i/Aggregation <>)))))))
 
   ;; also handle varargs for convenience
diff --git a/src/metabase/query_processor/interface.clj b/src/metabase/query_processor/interface.clj
index b157b6773b5674ae4679911883dae681d6a1b3be..9691104f6d1c75b46a5991f39a39e8f735e31b5d 100644
--- a/src/metabase/query_processor/interface.clj
+++ b/src/metabase/query_processor/interface.clj
@@ -177,12 +177,13 @@
                                unit   :- DatetimeValueUnit])
 
 
-(declare RValue)
+(declare RValue Aggregation)
 
 (def ^:private ExpressionOperator (s/named (s/enum :+ :- :* :/) "Valid expression operator"))
 
 (s/defrecord Expression [operator        :- ExpressionOperator
-                         args            :- [(s/recursive #'RValue)]])
+                         args            :- [(s/cond-pre (s/recursive #'RValue)
+                                                         (s/recursive #'Aggregation))]])
 
 (def AnyField
   "Schema for a `FieldPlaceholder`, `AgRef`, or `Expression`."
@@ -244,7 +245,8 @@
 
 (s/defrecord AggregationWithField [aggregation-type :- (s/named (s/enum :avg :count :cumulative-sum :distinct :max :min :stddev :sum)
                                                                 "Valid aggregation type")
-                                   field            :- FieldPlaceholderOrExpressionRef])
+                                   field            :- (s/cond-pre FieldPlaceholderOrExpressionRef
+                                                                   Expression)])
 
 (defn- valid-aggregation-for-driver? [{:keys [aggregation-type]}]
   (when (= aggregation-type :stddev)
@@ -254,7 +256,7 @@
 (def Aggregation
   "Schema for an `aggregation` subclause in an MBQL query."
   (s/constrained
-   (s/cond-pre AggregationWithField AggregationWithoutField)
+   (s/cond-pre AggregationWithField AggregationWithoutField Expression)
    valid-aggregation-for-driver?
    "standard-deviation-aggregations is not supported by this driver."))
 
diff --git a/src/metabase/query_processor/resolve.clj b/src/metabase/query_processor/resolve.clj
index 903ab942b584676ea08bdbeaec27189e6f021ba6..a27df5592f9e7590cfc259d7b28007dc4acc46dc 100644
--- a/src/metabase/query_processor/resolve.clj
+++ b/src/metabase/query_processor/resolve.clj
@@ -39,12 +39,15 @@
 (defprotocol ^:private IResolve
   (^:private unresolved-field-id ^Integer [this]
    "Return the unresolved Field ID associated with this object, if any.")
+
   (^:private fk-field-id ^Integer [this]
    "Return a the FK Field ID (for joining) associated with this object, if any.")
+
   (^:private resolve-field [this, ^clojure.lang.IPersistentMap field-id->field]
    "This method is called when walking the Query after fetching `Fields`.
     Placeholder objects should lookup the relevant Field in FIELD-ID->FIELDS and
-    return their expanded form. Other objects should just return themselves.a")
+    return their expanded form. Other objects should just return themselves.")
+
   (resolve-table [this, ^clojure.lang.IPersistentMap fk-id+table-id->tables]
    "Called when walking the Query after `Fields` have been resolved and `Tables` have been fetched.
     Objects like `Fields` can add relevant information like the name of their `Table`."))
@@ -194,7 +197,7 @@
         expanded-query-dict
         ;; Otherwise fetch + resolve the Fields in question
         (let [fields (->> (u/key-by :id (db/select [field/Field :name :display_name :base_type :special_type :visibility_type :table_id :parent_id :description :id]
-                                          :visibility_type [:not-in ["sensitive"]]
+                                          :visibility_type [:not= "sensitive"]
                                           :id [:in field-ids]))
                           (m/map-vals rename-mb-field-keys)
                           (m/map-vals #(assoc % :parent (when-let [parent-id (:parent-id %)]
@@ -204,7 +207,7 @@
            ;; Those will be used for Table resolution in the next step.
            (update expanded-query-dict :table-ids set/union (set (map :table-id (vals fields))))
            ;; Walk the query and resolve all fields
-           (walk/postwalk #(resolve-field % fields))
+           (walk/postwalk (u/rpartial resolve-field fields))
            ;; Recurse in case any new (nested) unresolved fields were found.
            (recur (dec max-iterations))))))))
 
@@ -218,10 +221,8 @@
                            [:target-table.name   :target-table-name]
                            [:target-table.schema :target-table-schema]]
                :from      [[field/Field :source-fk]]
-               :left-join [[field/Field :target-pk]
-                           [:= :source-fk.fk_target_field_id :target-pk.id]
-                           [Table :target-table]
-                           [:= :target-pk.table_id :target-table.id]]
+               :left-join [[field/Field :target-pk] [:= :source-fk.fk_target_field_id :target-pk.id]
+                           [Table :target-table]    [:= :target-pk.table_id :target-table.id]]
                :where     [:and [:in :source-fk.id      (set fk-field-ids)]
                                 [:=  :source-fk.table_id     source-table-id]
                                 (db/isa :source-fk.special_type :type/FK)]})))
diff --git a/test/metabase/driver/druid_test.clj b/test/metabase/driver/druid_test.clj
index 752e544439e6a0ae3c4be7a38c269a316d1cf4e5..9f1f897c3a339de5ebff830c9163991f868ad914 100644
--- a/test/metabase/driver/druid_test.clj
+++ b/test/metabase/driver/druid_test.clj
@@ -2,9 +2,12 @@
   (:require [cheshire.core :as json]
             [expectations :refer :all]
             [metabase.query-processor :as qp]
+            [metabase.query-processor.expand :as ql]
+            [metabase.query-processor-test :refer [rows]]
             [metabase.test.data :as data]
             [metabase.test.data.datasets :as datasets, :refer [expect-with-engine]]
-            [metabase.timeseries-query-processor-test :as timeseries-qp-test]))
+            [metabase.timeseries-query-processor-test :as timeseries-qp-test]
+            [metabase.query :as q]))
 
 (def ^:const ^:private ^String native-query-1
   (json/generate-string
@@ -59,3 +62,123 @@
 (expect-with-engine :druid
   :completed
   (:status (process-native-query native-query-2)))
+
+
+;;; +------------------------------------------------------------------------------------------------------------------------+
+;;; |                                                   MATH AGGREGATIONS                                                    |
+;;; +------------------------------------------------------------------------------------------------------------------------+
+
+(defmacro ^:private druid-query-returning-rows {:style/indent 0} [& body]
+  `(rows (timeseries-qp-test/with-flattened-dbdef
+           (qp/process-query {:database (data/id)
+                              :type     :query
+                              :query    (data/query ~'checkins
+                                          ~@body)}))))
+
+;; sum, *
+(expect-with-engine :druid
+  [["1" 110688.0]
+   ["2" 616708.0]
+   ["3" 179661.0]
+   ["4"  86284.0]]
+  (druid-query-returning-rows
+    (ql/aggregation (ql/sum (ql/* $id $venue_price)))
+    (ql/breakout $venue_price)))
+
+;; min, +
+(expect-with-engine :druid
+  [["1"  4.0]
+   ["2"  3.0]
+   ["3"  8.0]
+   ["4" 12.0]]
+  (druid-query-returning-rows
+    (ql/aggregation (ql/min (ql/+ $id $venue_price)))
+    (ql/breakout $venue_price)))
+
+;; max, /
+(expect-with-engine :druid
+  [["1" 1000.0]
+   ["2"  499.5]
+   ["3"  332.0]
+   ["4"  248.25]]
+  (druid-query-returning-rows
+    (ql/aggregation (ql/max (ql// $id $venue_price)))
+    (ql/breakout $venue_price)))
+
+;; avg, -
+(expect-with-engine :druid
+  [["1" 500.85067873303166]
+   ["2" 1002.7772357723577]
+   ["3" 1562.2695652173913]
+   ["4" 1760.8979591836735]]
+  (druid-query-returning-rows
+    (ql/aggregation (ql/avg (ql/* $id $venue_price)))
+    (ql/breakout $venue_price)))
+
+;; post-aggregation math w/ 2 args: count + sum
+(expect-with-engine :druid
+  [["1"  442.0]
+   ["2" 1845.0]
+   ["3"  460.0]
+   ["4"  245.0]]
+  (druid-query-returning-rows
+    (ql/aggregation (ql/+ (ql/count $id)
+                          (ql/sum $venue_price)))
+    (ql/breakout $venue_price)))
+
+;; post-aggregation math w/ 3 args: count + sum + count
+(expect-with-engine :druid
+  [["1"  663.0]
+   ["2" 2460.0]
+   ["3"  575.0]
+   ["4"  294.0]]
+  (druid-query-returning-rows
+    (ql/aggregation (ql/+ (ql/count $id)
+                          (ql/sum $venue_price)
+                          (ql/count $venue_price)))
+    (ql/breakout $venue_price)))
+
+;; post-aggregation math w/ a constant: count * 10
+(expect-with-engine :druid
+  [["1" 2210.0]
+   ["2" 6150.0]
+   ["3" 1150.0]
+   ["4"  490.0]]
+  (druid-query-returning-rows
+    (ql/aggregation (ql/* (ql/count $id)
+                          10))
+    (ql/breakout $venue_price)))
+
+;; nested post-aggregation math: count + (count * sum)
+(expect-with-engine :druid
+  [["1"  49062.0]
+   ["2" 757065.0]
+   ["3"  39790.0]
+   ["4"  9653.0]]
+  (druid-query-returning-rows
+    (ql/aggregation (ql/+ (ql/count $id)
+                          (ql/* (ql/count $id)
+                                (ql/sum $venue_price))))
+    (ql/breakout $venue_price)))
+
+;; post-aggregation math w/ avg: count + avg
+(expect-with-engine :druid
+  [["1"  721.8506787330316]
+   ["2" 1116.388617886179]
+   ["3"  635.7565217391304]
+   ["4"  489.2244897959184]]
+  (druid-query-returning-rows
+    (ql/aggregation (ql/+ (ql/count $id)
+                          (ql/avg $id)))
+    (ql/breakout $venue_price)))
+
+;; post aggregation math + math inside aggregations: max(venue_price) + min(venue_price - id)
+(expect-with-engine :druid
+  [["1" -998.0]
+   ["2" -995.0]
+   ["3" -990.0]
+   ["4" -985.0]]
+  (druid-query-returning-rows
+    (ql/aggregation (ql/+ (ql/max $venue_price)
+                          (ql/min (ql/- $venue_price $id))))
+    (ql/breakout $venue_price)))