From f00918c7b70c4ba8d854e29c4265aeeecc1fd476 Mon Sep 17 00:00:00 2001
From: Braden Shepherdson <braden@metabase.com>
Date: Tue, 30 May 2023 17:42:13 -0400
Subject: [PATCH] [MLv2] Support `:case` expressions in aggregations (#31088)

Fixes #29935.

Part of the issue here was incorrect hand-rolling of the second
argument to `:case`, the default value. In the failing test case given
in #29935, it was written as `{:default 0}`. But that's accidentally
transferring for legacy MBQL works, where the default is an option and
the options come last rather than first in a clause.

This also fixes `lib.common/->op-arg` to make sure it recurses
properly. It wasn't recursing into lists or MBQL clauses, which left
unresolved `(fn [query stage] ...)` functions in eg. `:case`
alternatives, since those are nested inside lists.
---
 src/metabase/lib/common.cljc                  | 13 +++++-
 test/metabase/lib/aggregation_test.cljc       | 40 +++++++++++++++++++
 .../query_processor_test/test_mlv2.clj        |  8 +---
 3 files changed, 52 insertions(+), 9 deletions(-)

diff --git a/src/metabase/lib/common.cljc b/src/metabase/lib/common.cljc
index 284253999ad..34eec2cc3f0 100644
--- a/src/metabase/lib/common.cljc
+++ b/src/metabase/lib/common.cljc
@@ -28,8 +28,17 @@
   :hierarchy lib.hierarchy/hierarchy)
 
 (defmethod ->op-arg :default
-  [_query _stage-number x]
-  x)
+  [query stage-number x]
+  (if (and (vector? x)
+           (keyword? (first x)))
+    ;; MBQL clause
+    (mapv #(->op-arg query stage-number %) x)
+    ;; Something else - just return it
+    x))
+
+(defmethod ->op-arg :dispatch-type/sequential
+  [query stage-number xs]
+  (mapv #(->op-arg query stage-number %) xs))
 
 (defmethod ->op-arg :metadata/field
   [_query _stage-number field-metadata]
diff --git a/test/metabase/lib/aggregation_test.cljc b/test/metabase/lib/aggregation_test.cljc
index 85f75de8db7..c089d9ff810 100644
--- a/test/metabase/lib/aggregation_test.cljc
+++ b/test/metabase/lib/aggregation_test.cljc
@@ -651,3 +651,43 @@
           first-stage (lib.util/query-stage query 0)]
       (is (= 2 (count (:stages query))))
       (is (contains? first-stage :order-by)))))
+
+(deftest ^:parallel aggregation-with-case-expression-metadata-test
+  (let [query (-> (lib/query-for-table-name meta/metadata-provider "VENUES")
+                  (lib/limit 4)
+                  (lib/breakout (lib/field (meta/id :venues :category-id)))
+                  (lib/aggregate (lib/sum (lib/case [[(lib/< (lib/field (meta/id :venues :price)) 2)
+                                                      (lib/field (meta/id :venues :price))]]
+                                            0))))]
+    (is (=? [{:description              nil
+              :lib/type                 :metadata/field
+              :table-id                 (meta/id :venues)
+              :name                     "CATEGORY_ID"
+              :base-type                :type/Integer
+              :semantic-type            :type/FK
+              :database-type            "INTEGER"
+              :effective-type           :type/Integer
+              :lib/source               :source/breakouts
+              :lib/source-column-alias  "CATEGORY_ID"
+              :lib/source-uuid          string?
+              :fk-target-field-id       (meta/id :categories :id)
+              :custom-position          0
+              :active                   true
+              :id                       (meta/id :venues :category-id)
+              :parent-id                nil
+              :visibility-type          :normal
+              :lib/desired-column-alias "CATEGORY_ID"
+              :display-name             "Category ID"
+              :has-field-values         :none
+              :target                   nil
+              :preview-display          true
+              :fingerprint              {:global {:distinct-count 28, :nil% 0.0}}}
+             {:lib/type                 :metadata/field
+              :base-type                :type/Integer
+              :name                     "sum_case"
+              :display-name             "Sum of Case"
+              :lib/source               :source/aggregations
+              :lib/source-uuid          string?
+              :lib/source-column-alias  "sum_case"
+              :lib/desired-column-alias "sum_case"}]
+            (lib.metadata.calculation/metadata query)))))
diff --git a/test/metabase/query_processor_test/test_mlv2.clj b/test/metabase/query_processor_test/test_mlv2.clj
index c00bb64bc34..f20273392b4 100644
--- a/test/metabase/query_processor_test/test_mlv2.clj
+++ b/test/metabase/query_processor_test/test_mlv2.clj
@@ -42,13 +42,7 @@
      #{:datetime-add :datetime-subtract :convert-timezone}
      (mbql.u/match-one &match
        [_tag (_literal :guard string?) & _]
-       "#29910"))
-   ;; #29935: metadata for an `:aggregation` with a `:case` expression not working
-   (mbql.u/match-one legacy-query
-     {:aggregation aggregations}
-     (mbql.u/match-one aggregations
-       :case
-       "#29935"))))
+       "#29910"))))
 
 (defn- test-mlv2-metadata [original-query _qp-metadata]
   {:pre [(map? original-query)]}
-- 
GitLab