[match] Remove old match implementations based on clojure.core.match (#73209)

* [match] Remove old match implementations based on clojure.core.match

* Rename match-lite to match-one and replace-lite to replace

* Stop using clojure.core.match completely

* Move metabase.lib.util.match to metabase.util.match

* Remove clojure.core.match dependency

* Update docstrings

* Address comments
This commit is contained in:
Oleksandr Yakushev
2026-05-11 00:58:50 +03:00
committed by GitHub
parent 6404c3c029
commit f9ce3f291d
85 changed files with 652 additions and 1020 deletions

View File

@@ -39,7 +39,7 @@
;; ;;
;; TODO (cam): most of these either have a ton of things that need to be fixed or cause issues ;; TODO (cam): most of these either have a ton of things that need to be fixed or cause issues
;; with [[metabase.lib.util.match/match]] and friends -- we need to write a custom hook for those macros so Kondo ;; with [[metabase.util.match/match]] and friends -- we need to write a custom hook for those macros so Kondo
;; can understand them. ;; can understand them.
#_:docstring-leading-trailing-whitespace #_:docstring-leading-trailing-whitespace
@@ -231,10 +231,9 @@
;; misc config for built-in linters ;; misc config for built-in linters
;; ;;
:invalid-arity {:skip-args [metabase.lib.util.match/match :invalid-arity {:skip-args [metabase.util.match/match-one
metabase.lib.util.match/match-lite metabase.util.match/match-many
metabase.lib.util.match/match-many metabase.util.match/replace]} ; TODO (cam): can we fix these?
metabase.lib.util.match/replace-lite]} ; TODO (cam): can we fix these?
:refer-all {:level :warning, :exclude [clojure.test]} :refer-all {:level :warning, :exclude [clojure.test]}
:unused-referred-var {:exclude {compojure.core [GET DELETE POST PUT]}} :unused-referred-var {:exclude {compojure.core [GET DELETE POST PUT]}}
@@ -250,20 +249,15 @@
instaparse.core/transform instaparse.core/transform
(metabase.server.streaming-response/streaming-response) (metabase.server.streaming-response/streaming-response)
(metabase.driver.druid.query-processor-test/druid-query-returning-rows) (metabase.driver.druid.query-processor-test/druid-query-returning-rows)
(metabase.lib.util.match/match) (metabase.util.match/match-one)
(metabase.lib.util.match/match-one) (metabase.util.match/match-many)
(metabase.lib.util.match/match-lite) (metabase.util.match/replace-in)
(metabase.lib.util.match/match-many) (metabase.util.match/replace)
(metabase.lib.util.match/replace)
(metabase.lib.util.match/replace-in)
(metabase.lib.util.match/replace-lite)
(metabase.driver-api.core/match) (metabase.driver-api.core/match)
(metabase.driver-api.core/match-one) (metabase.driver-api.core/match-one)
(metabase.driver-api.core/match-lite)
(metabase.driver-api.core/match-many) (metabase.driver-api.core/match-many)
(metabase.driver-api.core/replace) (metabase.driver-api.core/replace)
(metabase.driver-api.core/replace-in) (metabase.driver-api.core/replace-in)
(metabase.driver-api.core/replace-lite)
(metabase.query-processor.middleware.cache-backend.interface/with-cached-results) (metabase.query-processor.middleware.cache-backend.interface/with-cached-results)
(metabase.util.regex/rx [opt]) (metabase.util.regex/rx [opt])
(metabase.util/prog1 [<>]) (metabase.util/prog1 [<>])
@@ -315,9 +309,6 @@
metabase.lib.convert/->legacy-MBQL {:message "Conversion to legacy MBQL is discouraged, please use Lib to manipulate MBQL queries"} metabase.lib.convert/->legacy-MBQL {:message "Conversion to legacy MBQL is discouraged, please use Lib to manipulate MBQL queries"}
metabase.lib.core/->legacy-MBQL {:message "Conversion to legacy MBQL is discouraged, please use Lib to manipulate MBQL queries"} metabase.lib.core/->legacy-MBQL {:message "Conversion to legacy MBQL is discouraged, please use Lib to manipulate MBQL queries"}
metabase.lib.equality/find-column-indexes-for-refs {:message "Use lib.equality/closest-matches-in-metadata or lib.equality/find-closest-matches-for-refs instead of lib.equality/find-column-indexes-for-refs"} metabase.lib.equality/find-column-indexes-for-refs {:message "Use lib.equality/closest-matches-in-metadata or lib.equality/find-closest-matches-for-refs instead of lib.equality/find-column-indexes-for-refs"}
metabase.lib.util.match/match {:message "Use lib.util.match/match-many instead of lib.util.match/match."}
metabase.lib.util.match/match-one {:message "Use lib.util.match/match-lite instead of lib.util.match/match-one."}
metabase.lib.util.match/replace {:message "Use lib.util.match/replace-lite instead of lib.util.match/replace."}
metabase-enterprise.transforms.interface/execute! {:message "Use metabase-enterprise.transforms.execute/execute! instead of metabase-enterprise.transforms.interface/execute!"} metabase-enterprise.transforms.interface/execute! {:message "Use metabase-enterprise.transforms.execute/execute! instead of metabase-enterprise.transforms.interface/execute!"}
next.jdbc/active-tx? {:message "Use metabase.app-db.core/in-transaction? to see if we are in a transaction"} next.jdbc/active-tx? {:message "Use metabase.app-db.core/in-transaction? to see if we are in a transaction"}
toucan2.core/debug {:message "Don't commit code using t2/debug"} toucan2.core/debug {:message "Don't commit code using t2/debug"}
@@ -351,7 +342,6 @@
clj-time.coerce time.coerce clj-time.coerce time.coerce
clj-time.format time.format clj-time.format time.format
clojure.core.async a clojure.core.async a
clojure.core.match clojure.core.match
clojure.data data clojure.data data
clojure.math.combinatorics math.combo clojure.math.combinatorics math.combo
clojure.pprint pprint clojure.pprint pprint

View File

@@ -1084,8 +1084,7 @@
metabase.lib.schema.util metabase.lib.schema.util
metabase.lib.schema.validate metabase.lib.schema.validate
metabase.lib.types.isa metabase.lib.types.isa
metabase.lib.util metabase.lib.util}
metabase.lib.util.match}
:uses #{config :uses #{config
graph graph
legacy-mbql legacy-mbql

View File

@@ -43,7 +43,6 @@
with-meta [[:default]] with-meta [[:default]]
;; library stuff. Libraries that specify their own `:style/indent` specs are included in the section after this ;; library stuff. Libraries that specify their own `:style/indent` specs are included in the section after this
;; since it is generated automatically. ;; since it is generated automatically.
clojure.core.match/match [[:block 1]]
clojure.test.check.properties/for-all [[:block 1]] clojure.test.check.properties/for-all [[:block 1]]
clojure.test.check.properties/for-all* [[:block 1]] clojure.test.check.properties/for-all* [[:block 1]]
clojure.test.check/quick-check [[:block 1]] clojure.test.check/quick-check [[:block 1]]
@@ -118,12 +117,9 @@
metabase.collections.models.collection-test/with-personal-and-impersonal-collections [[:block 1]] metabase.collections.models.collection-test/with-personal-and-impersonal-collections [[:block 1]]
metabase.config/build-type-case [[:block 0]] metabase.config/build-type-case [[:block 0]]
metabase.dashboards.models.dashboard-test/with-dash-in-collection [[:inner 0]] metabase.dashboards.models.dashboard-test/with-dash-in-collection [[:inner 0]]
metabase.driver-api.core/match [[:inner 0]]
metabase.driver-api.core/match-one [[:inner 0]] metabase.driver-api.core/match-one [[:inner 0]]
metabase.driver-api.core/match-lite [[:inner 0]]
metabase.driver-api.core/match-many [[:inner 0]] metabase.driver-api.core/match-many [[:inner 0]]
metabase.driver-api.core/replace [[:inner 0]] metabase.driver-api.core/replace [[:inner 0]]
metabase.driver-api.core/replace-lite [[:inner 0]]
metabase.driver-api.core/replace-in [[:inner 0]] metabase.driver-api.core/replace-in [[:inner 0]]
metabase.driver.bigquery-cloud-sdk.query-processor/with-temporal-type [[:default]] metabase.driver.bigquery-cloud-sdk.query-processor/with-temporal-type [[:default]]
metabase.driver.druid.client/DELETE [[:default]] metabase.driver.druid.client/DELETE [[:default]]
@@ -156,13 +152,10 @@
metabase.lib.test-util.macros/$ids [[:inner 0]] metabase.lib.test-util.macros/$ids [[:inner 0]]
metabase.lib.test-util.macros/mbql-5-query [[:inner 0]] metabase.lib.test-util.macros/mbql-5-query [[:inner 0]]
metabase.lib.test-util.macros/mbql-query [[:inner 0]] metabase.lib.test-util.macros/mbql-query [[:inner 0]]
metabase.lib.util.match/match [[:inner 0]] metabase.util.match/match-one [[:inner 0]]
metabase.lib.util.match/match-one [[:inner 0]] metabase.util.match/match-many [[:inner 0]]
metabase.lib.util.match/match-lite [[:inner 0]] metabase.util.match/replace [[:inner 0]]
metabase.lib.util.match/match-many [[:inner 0]] metabase.util.match/replace-in [[:inner 0]]
metabase.lib.util.match/replace [[:inner 0]]
metabase.lib.util.match/replace-lite [[:inner 0]]
metabase.lib.util.match/replace-in [[:inner 0]]
metabase.models.interface/define-batched-hydration-method [[:inner 0]] metabase.models.interface/define-batched-hydration-method [[:inner 0]]
metabase.models.interface/define-simple-hydration-method [[:inner 0]] metabase.models.interface/define-simple-hydration-method [[:inner 0]]
metabase.parameters.chain-filter-test/with-clean-field-values-for-field [[:block 1]] metabase.parameters.chain-filter-test/with-clean-field-values-for-field [[:block 1]]

View File

@@ -41,11 +41,6 @@
(eval . (put-clojure-indent 'c/step 1)) (eval . (put-clojure-indent 'c/step 1))
(eval . (put-clojure-indent 'impl/test-migrations 2)) (eval . (put-clojure-indent 'impl/test-migrations 2))
(eval . (put-clojure-indent 'let-404 0)) (eval . (put-clojure-indent 'let-404 0))
(eval . (put-clojure-indent 'lib.schema.match/match '(:defn)))
(eval . (put-clojure-indent 'lib.schema.match/match-one '(:defn)))
(eval . (put-clojure-indent 'lib.schema.match/replace '(:defn)))
(eval . (put-clojure-indent 'lib.schema.match/replace-in '(:defn)))
(eval . (put-clojure-indent 'lib.util.match/replace '(:defn)))
(eval . (put-clojure-indent 'macros/case 0)) (eval . (put-clojure-indent 'macros/case 0))
(eval . (put-clojure-indent 'match 1)) (eval . (put-clojure-indent 'match 1))
(eval . (put-clojure-indent 'mt/dataset 1)) (eval . (put-clojure-indent 'mt/dataset 1))

View File

@@ -145,7 +145,6 @@
org.clojure/clojure {:mvn/version "1.12.3"} org.clojure/clojure {:mvn/version "1.12.3"}
org.clojure/core.async {:mvn/version "1.7.701" org.clojure/core.async {:mvn/version "1.7.701"
:exclusions [org.clojure/tools.reader]} :exclusions [org.clojure/tools.reader]}
org.clojure/core.match {:mvn/version "1.1.0"}
org.clojure/core.memoize {:mvn/version "1.1.266"} ; useful FIFO, LRU, etc. caching mechanisms org.clojure/core.memoize {:mvn/version "1.1.266"} ; useful FIFO, LRU, etc. caching mechanisms
org.clojure/data.csv {:mvn/version "1.1.0"} ; CSV parsing / generation org.clojure/data.csv {:mvn/version "1.1.0"} ; CSV parsing / generation
org.clojure/data.xml {:mvn/version "0.2.0-alpha10"} ; XML parsing / generation org.clojure/data.xml {:mvn/version "0.2.0-alpha10"} ; XML parsing / generation
@@ -646,8 +645,8 @@
:implicit-dependencies :implicit-dependencies
;; too many false positives for now ;; too many false positives for now
:unused-ret-vals :unused-ret-vals
;; hard to clear on for lib.util.match macros, and disabling the linter just there ;; hard to clear on for metabase.util.match macros, and disabling the linter just
;; doesn't seem to work ;; there doesn't seem to work
:unused-meta-on-macro :unused-meta-on-macro
;; Kondo lints this for us anyway, and this isn't as easy to configure. ;; Kondo lints this for us anyway, and this isn't as easy to configure.
:wrong-arity :wrong-arity

View File

@@ -17,8 +17,8 @@
[metabase.driver :as driver] [metabase.driver :as driver]
[metabase.legacy-mbql.normalize :as mbql.normalize] [metabase.legacy-mbql.normalize :as mbql.normalize]
[metabase.legacy-mbql.util :as mbql.u] [metabase.legacy-mbql.util :as mbql.u]
[metabase.lib.util.match :as lib.util.match]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.match :as match]
[portal.api] [portal.api]
[toucan2.core :as t2])) [toucan2.core :as t2]))
@@ -123,7 +123,7 @@
(symbol (format "#_\"%s.%s\"" field-name table-name))))) (symbol (format "#_\"%s.%s\"" field-name table-name)))))
(field-id->name-form [field-id] (field-id->name-form [field-id]
(list 'do (add-name-to-field-id field-id) field-id))] (list 'do (add-name-to-field-id field-id) field-id))]
(lib.util.match/replace-lite form (match/replace form
[:field (id :guard pos-int?) opts] [:field (id :guard pos-int?) opts]
[:field id (add-name-to-field-id id) (cond-> opts [:field id (add-name-to-field-id id) (cond-> opts
(pos-int? (:source-field opts)) (pos-int? (:source-field opts))
@@ -258,7 +258,7 @@
coll)) coll))
(defn- can-symbolize? [x] (defn- can-symbolize? [x]
(lib.util.match/match-lite x (match/match-one x
(_ :guard string?) (_ :guard string?)
(not (re-find #"\s+" x)) (not (re-find #"\s+" x))
@@ -285,7 +285,7 @@
(defn- expand [form table] (defn- expand [form table]
(try (try
(lib.util.match/replace-lite form (match/replace form
(:and [:field (id :guard pos-int?) nil] (:and [:field (id :guard pos-int?) nil]
(_ :guard can-symbolize?)) (_ :guard can-symbolize?))
(let [[table-name field-name] (field-and-table-name id) (let [[table-name field-name] (field-and-table-name id)
@@ -346,10 +346,10 @@
e))))) e)))))
(defn- no-$ [x] (defn- no-$ [x]
(lib.util.match/replace-lite x [::$ & args] (into [::no-$] args))) (match/replace x [::$ & args] (into [::no-$] args)))
(defn- symbolize [form] (defn- symbolize [form]
(lib.util.match/replace-lite form (match/replace form
[::-> x y] [::-> x y]
(symbol (format "%s->%s" (symbolize x) (str/replace (symbolize y) #"^\$" ""))) (symbol (format "%s->%s" (symbolize x) (str/replace (symbolize y) #"^\$" "")))

View File

@@ -14,12 +14,12 @@
[clojure.set :as set] [clojure.set :as set]
[metabase.api.common :as api] [metabase.api.common :as api]
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.util.match :as lib.util.match]
[metabase.models.interface :as mi] [metabase.models.interface :as mi]
[metabase.permissions.core :as perms] [metabase.permissions.core :as perms]
[metabase.query-processor.preprocess :as qp.preprocess] [metabase.query-processor.preprocess :as qp.preprocess]
[metabase.util.i18n :refer [tru]] [metabase.util.i18n :refer [tru]]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[toucan2.core :as t2])) [toucan2.core :as t2]))
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
@@ -177,7 +177,7 @@
[user-id card permissions-blocking permissions-granting] [user-id card permissions-blocking permissions-granting]
(let [query (-> card :dataset_query qp.preprocess/preprocess) (let [query (-> card :dataset_query qp.preprocess/preprocess)
query-tables (lib/all-source-table-ids query) query-tables (lib/all-source-table-ids query)
native? (lib.util.match/match-lite query {:native &truthy} true)] native? (match/match-one query {:native &truthy} true)]
(->> (->>
(cond (cond
native? native?

View File

@@ -3,8 +3,8 @@
[metabase-enterprise.sandbox.api.table :as table] [metabase-enterprise.sandbox.api.table :as table]
[metabase-enterprise.sandbox.query-processor.middleware.sandboxing :as sandboxing] [metabase-enterprise.sandbox.query-processor.middleware.sandboxing :as sandboxing]
[metabase.api.common :as api] [metabase.api.common :as api]
[metabase.lib.util.match :as lib.util.match]
[metabase.premium-features.core :refer [defenterprise]] [metabase.premium-features.core :refer [defenterprise]]
[metabase.util.match :as match]
[metabase.warehouse-schema.models.field :as field] [metabase.warehouse-schema.models.field :as field]
[toucan2.core :as t2])) [toucan2.core :as t2]))
@@ -70,7 +70,7 @@
(into {} (for [[k v] attribute_remappings (into {} (for [[k v] attribute_remappings
;; get attribute that map to fields of the same table ;; get attribute that map to fields of the same table
:when (contains? field-ids :when (contains? field-ids
(lib.util.match/match-lite v (match/match-one v
;; new style with {:stage-number } ;; new style with {:stage-number }
[:dimension [:field field-id _] _] field-id [:dimension [:field field-id _] _] field-id
;; old style without stage number ;; old style without stage number

View File

@@ -20,7 +20,6 @@
[metabase.lib.schema.metadata :as lib.schema.metadata] [metabase.lib.schema.metadata :as lib.schema.metadata]
[metabase.lib.schema.parameter :as lib.schema.parameter] [metabase.lib.schema.parameter :as lib.schema.parameter]
[metabase.lib.schema.util :as lib.schema.util] [metabase.lib.schema.util :as lib.schema.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.premium-features.core :as premium-features :refer [defenterprise]] [metabase.premium-features.core :as premium-features :refer [defenterprise]]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
@@ -32,6 +31,7 @@
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
^{:clj-kondo/ignore [:discouraged-namespace]} ^{:clj-kondo/ignore [:discouraged-namespace]}
[toucan2.core :as t2])) [toucan2.core :as t2]))
@@ -94,7 +94,7 @@
[metadata-providerable :- ::lib.schema.metadata/metadata-providerable [metadata-providerable :- ::lib.schema.metadata/metadata-providerable
target-field-clause :- ::lib.schema.parameter/target] target-field-clause :- ::lib.schema.parameter/target]
;; parameter targets still use legacy field refs for whatever wacko reason ;; parameter targets still use legacy field refs for whatever wacko reason
(when-let [field-id (lib.util.match/match-lite target-field-clause (when-let [field-id (match/match-one target-field-clause
[:field (field-id :guard pos-int?) _opts] field-id)] [:field (field-id :guard pos-int?) _opts] field-id)]
(:base-type (lib.metadata/field metadata-providerable field-id)))) (:base-type (lib.metadata/field metadata-providerable field-id))))
@@ -409,7 +409,7 @@
:feature :sandboxes :feature :sandboxes
[{::keys [original-metadata] :as query} rff] [{::keys [original-metadata] :as query} rff]
(fn merge-sandboxing-metadata-rff* [metadata] (fn merge-sandboxing-metadata-rff* [metadata]
(let [metadata (assoc metadata :is_sandboxed (boolean (lib.util.match/match-lite query (let [metadata (assoc metadata :is_sandboxed (boolean (match/match-one query
{:query-permissions/sandboxed-table &truthy} true))) {:query-permissions/sandboxed-table &truthy} true)))
metadata (if original-metadata metadata (if original-metadata
(merge-metadata original-metadata metadata) (merge-metadata original-metadata metadata)

View File

@@ -8,7 +8,6 @@
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.schema :as lib.schema] [metabase.lib.schema :as lib.schema]
[metabase.lib.test-metadata :as meta] [metabase.lib.test-metadata :as meta]
[metabase.lib.util.match :as lib.util.match]
[metabase.permissions-rest.data-permissions.graph :as data-perms.graph] [metabase.permissions-rest.data-permissions.graph :as data-perms.graph]
[metabase.permissions.core :as perms] [metabase.permissions.core :as perms]
[metabase.permissions.test-util :as perms.test-util] [metabase.permissions.test-util :as perms.test-util]
@@ -17,7 +16,8 @@
[metabase.query-processor.streaming-test :as streaming-test] [metabase.query-processor.streaming-test :as streaming-test]
[metabase.test :as mt] [metabase.test :as mt]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.malli :as mu]) [metabase.util.malli :as mu]
[metabase.util.match :as match])
(:import (:import
(clojure.lang ExceptionInfo))) (clojure.lang ExceptionInfo)))
@@ -33,7 +33,7 @@
(f))))) (f)))))
(defn- remove-metadata [m] (defn- remove-metadata [m]
(lib.util.match/replace-lite m (match/replace m
{:source-metadata _} {:source-metadata _}
(remove-metadata (dissoc &match :source-metadata)))) (remove-metadata (dissoc &match :source-metadata))))

View File

@@ -14,7 +14,6 @@
[metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata :as lib.metadata]
[metabase.lib.test-util :as lib.tu] [metabase.lib.test-util :as lib.tu]
[metabase.lib.test-util.notebook-helpers :as lib.tu.notebook] [metabase.lib.test-util.notebook-helpers :as lib.tu.notebook]
[metabase.lib.util.match :as lib.util.match]
[metabase.permissions.models.data-permissions :as data-perms] [metabase.permissions.models.data-permissions :as data-perms]
[metabase.permissions.models.permissions :as perms] [metabase.permissions.models.permissions :as perms]
[metabase.permissions.models.permissions-group :as perms-group] [metabase.permissions.models.permissions-group :as perms-group]
@@ -37,6 +36,7 @@
[metabase.util :as u] [metabase.util :as u]
[metabase.util.honey-sql-2 :as h2x] [metabase.util.honey-sql-2 :as h2x]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.match :as match]
[toucan2.core :as t2])) [toucan2.core :as t2]))
(use-fixtures :once (fixtures/initialize :db)) (use-fixtures :once (fixtures/initialize :db))
@@ -162,7 +162,7 @@
;; TODO -- #19754 adds [[mt/remove-source-metadata]] that can be used here (once it gets merged) ;; TODO -- #19754 adds [[mt/remove-source-metadata]] that can be used here (once it gets merged)
(defn- remove-metadata [m] (defn- remove-metadata [m]
(lib.util.match/replace-lite m (match/replace m
{:source-metadata _} {:source-metadata _}
(remove-metadata (dissoc &match :source-metadata)))) (remove-metadata (dissoc &match :source-metadata))))

View File

@@ -7,12 +7,12 @@
[metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata :as lib.metadata]
[metabase.lib.test-metadata :as meta] [metabase.lib.test-metadata :as meta]
[metabase.lib.test-util :as lib.tu] [metabase.lib.test-util :as lib.tu]
[metabase.lib.util.match :as lib.util.match]
^{:clj-kondo/ignore [:deprecated-namespace]} [metabase.query-processor.store :as qp.store] ^{:clj-kondo/ignore [:deprecated-namespace]} [metabase.query-processor.store :as qp.store]
[metabase.query-processor.timezone :as qp.timezone] [metabase.query-processor.timezone :as qp.timezone]
[metabase.query-processor.util.add-alias-info :as add] [metabase.query-processor.util.add-alias-info :as add]
[metabase.test :as mt] [metabase.test :as mt]
[metabase.util.honey-sql-2 :as h2x])) [metabase.util.honey-sql-2 :as h2x]
[metabase.util.match :as match]))
(def mock-temporal-fields-metadata-provider (def mock-temporal-fields-metadata-provider
(let [date-field (merge (meta/field-metadata :checkins :date) (let [date-field (merge (meta/field-metadata :checkins :date)
@@ -171,7 +171,7 @@
(defn- temporal-type-reconciliation-expected-value (defn- temporal-type-reconciliation-expected-value
[{:keys [field temporal-type expected-value honeysql-filter-fn num-args], :as _test-case}] [{:keys [field temporal-type expected-value honeysql-filter-fn num-args], :as _test-case}]
(let [field-literal? (lib.util.match/match-lite field [:field (_ :guard string?) _] true) (let [field-literal? (match/match-one field [:field (_ :guard string?) _] true)
mock-field (get mock-temporal-fields temporal-type) mock-field (get mock-temporal-fields temporal-type)
expected-identifier (cond-> (-> (h2x/identifier :field "ABC" (name temporal-type)) expected-identifier (cond-> (-> (h2x/identifier :field "ABC" (name temporal-type))
(vary-meta assoc ::bigquery.qp/do-not-qualify? true)) (vary-meta assoc ::bigquery.qp/do-not-qualify? true))

View File

@@ -1,7 +1,6 @@
(ns metabase.driver.druid.query-processor (ns metabase.driver.druid.query-processor
(:refer-clojure :exclude [every? mapv some get-in]) (:refer-clojure :exclude [every? mapv some get-in])
(:require (:require
[clojure.core.match :refer [match]]
[clojure.string :as str] [clojure.string :as str]
[metabase.driver-api.core :as driver-api] [metabase.driver-api.core :as driver-api]
[metabase.driver.common :as driver.common] [metabase.driver.common :as driver.common]
@@ -306,7 +305,7 @@
(defn- parse-filter [filter-clause] (defn- parse-filter [filter-clause]
;; strip out all the filters against temporal fields. Those are handled separately, as intervals ;; strip out all the filters against temporal fields. Those are handled separately, as intervals
(-> (driver-api/replace-lite filter-clause (-> (driver-api/replace filter-clause
[_ [:field _ {:temporal-unit &truthy}] & _] [_ [:field _ {:temporal-unit &truthy}] & _]
nil) nil)
;; TODO (Cam 8/18/25) -- I am 90% sure this is serving no useful purpose. ;; TODO (Cam 8/18/25) -- I am 90% sure this is serving no useful purpose.
@@ -328,7 +327,7 @@
"Adding `n` `:default` units doesn't make sense. So if an `:absoulte-datetime` has `:default` as its unit, add `n` "Adding `n` `:default` units doesn't make sense. So if an `:absoulte-datetime` has `:default` as its unit, add `n`
milliseconds, because that is the smallest unit Druid supports." milliseconds, because that is the smallest unit Druid supports."
[clause n] [clause n]
(driver-api/replace-lite clause (driver-api/replace clause
[:absolute-datetime t :default] [:absolute-datetime t :default]
[:absolute-datetime (u.date/add t :millisecond n) :millisecond] [:absolute-datetime (u.date/add t :millisecond n) :millisecond]
@@ -336,7 +335,7 @@
(add-datetime-units* clause n))) (add-datetime-units* clause n)))
(defn- ->absolute-timestamp ^java.time.temporal.Temporal [clause] (defn- ->absolute-timestamp ^java.time.temporal.Temporal [clause]
(driver-api/match-lite clause (driver-api/match-one clause
[:absolute-datetime t :default] [:absolute-datetime t :default]
t t
@@ -356,7 +355,7 @@
clauses, the methods are skipped entirely." clauses, the methods are skipped entirely."
{:arglists '([filter-clause])} {:arglists '([filter-clause])}
(fn [filter-clause] (fn [filter-clause]
(when (driver-api/match-lite filter-clause [:field _ {:temporal-unit &truthy}] true) (when (driver-api/match-one filter-clause [:field _ {:temporal-unit &truthy}] true)
(driver-api/dispatch-by-clause-name-or-class filter-clause)))) (driver-api/dispatch-by-clause-name-or-class filter-clause))))
(defmethod filter-clause->intervals :default (defmethod filter-clause->intervals :default
@@ -627,7 +626,7 @@
(defn- create-aggregation-clause (defn- create-aggregation-clause
[output-name ag-type ag-field args] [output-name ag-type ag-field args]
(let [output-name-kwd (keyword output-name)] (let [output-name-kwd (keyword output-name)]
(match [ag-type ag-field] (driver-api/match-one [ag-type ag-field]
;; For 'distinct values' queries (queries with a breakout by no aggregation) just aggregate by count, but name ;; For 'distinct values' queries (queries with a breakout by no aggregation) just aggregate by count, but name
;; it :___count so it gets discarded automatically ;; it :___count so it gets discarded automatically
[nil nil] [[(or output-name-kwd :___count)] {:aggregations [(ag:count (or output-name :___count))]}] [nil nil] [[(or output-name-kwd :___count)] {:aggregations [(ag:count (or output-name :___count))]}]
@@ -672,7 +671,8 @@
[:min _] [[(or output-name-kwd :min)] [:min _] [[(or output-name-kwd :min)]
{:aggregations [(ag:doubleMin ag-field (or output-name :min))]}] {:aggregations [(ag:doubleMin ag-field (or output-name :min))]}]
[:max _] [[(or output-name-kwd :max)] [:max _] [[(or output-name-kwd :max)]
{:aggregations [(ag:doubleMax ag-field (or output-name :max))]}]))) {:aggregations [(ag:doubleMax ag-field (or output-name :max))]}]
_ (throw (ex-info (str "Unknown aggregation tuple: " [ag-type ag-field]) {})))))
(defn ^:private ^:dynamic *query-unique-name-fn* (defn ^:private ^:dynamic *query-unique-name-fn*
[& _] [& _]
@@ -683,7 +683,7 @@
ag-clause :- driver-api/mbql.schema.Aggregation ag-clause :- driver-api/mbql.schema.Aggregation
druid-query] druid-query]
(let [output-name (*query-unique-name-fn* (driver-api/aggregation-name *query* ag-clause)) (let [output-name (*query-unique-name-fn* (driver-api/aggregation-name *query* ag-clause))
[ag-type ag-field & args] (driver-api/match-lite ag-clause [ag-type ag-field & args] (driver-api/match-one ag-clause
[:aggregation-options ag & _] (&recur ag) [:aggregation-options ag & _] (&recur ag)
_ &match)] _ &match)]
(if-not (isa? query-type ::ag-query) (if-not (isa? query-type ::ag-query)
@@ -703,7 +703,7 @@
(update :query (partial merge-with concat) ag-clauses)))))) (update :query (partial merge-with concat) ag-clauses))))))
(defn- deduplicate-aggregation-options [expression] (defn- deduplicate-aggregation-options [expression]
(driver-api/replace-lite expression (driver-api/replace expression
[:aggregation-options [:aggregation-options ag options-1] options-2] [:aggregation-options [:aggregation-options ag options-1] options-2]
[:aggregation-options ag (merge options-1 options-2)])) [:aggregation-options ag (merge options-1 options-2)]))
@@ -712,7 +712,7 @@
(defn- add-expression-aggregation-output-names (defn- add-expression-aggregation-output-names
[expression] [expression]
(driver-api/replace-lite expression (driver-api/replace expression
[:aggregation-options ag options] [:aggregation-options ag options]
(deduplicate-aggregation-options [:aggregation-options (add-expression-aggregation-output-names ag) options]) (deduplicate-aggregation-options [:aggregation-options (add-expression-aggregation-output-names ag) options])
@@ -730,7 +730,7 @@
(defn- expression-post-aggregation (defn- expression-post-aggregation
[[operator & args, :as expression]] [[operator & args, :as expression]]
(driver-api/match-lite expression (driver-api/match-one expression
;; If it's a named expression, we want to preserve the included name, so recurse, but merge in the name ;; If it's a named expression, we want to preserve the included name, so recurse, but merge in the name
[:aggregation-options ag _] [:aggregation-options ag _]
(assoc (expression-post-aggregation ag) :name (driver-api/aggregation-name *query* expression)) (assoc (expression-post-aggregation ag) :name (driver-api/aggregation-name *query* expression))
@@ -740,7 +740,7 @@
:name (driver-api/aggregation-name *query* expression) :name (driver-api/aggregation-name *query* expression)
:fn operator :fn operator
:fields (vec (for [arg args] :fields (vec (for [arg args]
(driver-api/match-lite arg (driver-api/match-one arg
(_ :guard number?) (_ :guard number?)
{:type :constant, :name (str &match), :value &match} {:type :constant, :name (str &match), :value &match}
@@ -796,7 +796,7 @@
(*query-unique-name-fn* (name projection))) (*query-unique-name-fn* (name projection)))
(reduce (reduce
(fn [druid-query aggregation] (fn [druid-query aggregation]
(driver-api/match-lite aggregation (driver-api/match-one aggregation
[:aggregation-options [#{:+ :- :/ :*} & _] _] [:aggregation-options [#{:+ :- :/ :*} & _] _]
(handle-expression-aggregation query-type &match druid-query) (handle-expression-aggregation query-type &match druid-query)
@@ -954,7 +954,7 @@
(defn- field-clause->name (defn- field-clause->name
[field-clause] [field-clause]
(driver-api/match-lite field-clause (driver-api/match-one field-clause
[:field (id :guard integer?) _] [:field (id :guard integer?) _]
(:name (driver-api/field (driver-api/metadata-provider) id)) (:name (driver-api/field (driver-api/metadata-provider) id))
@@ -1002,14 +1002,14 @@
(let [field (->rvalue field) (let [field (->rvalue field)
breakout-field (->rvalue breakout-field) breakout-field (->rvalue breakout-field)
sort-by-breakout? (= field breakout-field) sort-by-breakout? (= field breakout-field)
ag-field (driver-api/match-lite ag ag-field (driver-api/match-one ag
[:distinct & _] :distinct___count [:distinct & _] :distinct___count
[:aggregation-options _ {:name name}] name [:aggregation-options _ {:name name}] name
[:aggregation-options wrapped-ag _] (&recur wrapped-ag) [:aggregation-options wrapped-ag _] (&recur wrapped-ag)
[(ag-type :guard keyword?) & _] ag-type)] [(ag-type :guard keyword?) & _] ag-type)]
(when-not sort-by-breakout? (when-not sort-by-breakout?
(assert ag-field)) (assert ag-field))
(assoc-in druid-query [:query :metric] (match [sort-by-breakout? direction] (assoc-in druid-query [:query :metric] (driver-api/match-one [sort-by-breakout? direction]
[true :asc] {:type :alphaNumeric} [true :asc] {:type :alphaNumeric}
[true :desc] {:type :inverted, :metric {:type :alphaNumeric}} [true :desc] {:type :inverted, :metric {:type :alphaNumeric}}
[false :asc] {:type :inverted, :metric ag-field} [false :asc] {:type :inverted, :metric ag-field}
@@ -1027,7 +1027,7 @@
datetime" datetime"
[field] [field]
(when field (when field
(driver-api/match-lite field (driver-api/match-one field
[:field _id-or-name {:temporal-unit &truthy}] [:field _id-or-name {:temporal-unit &truthy}]
true true
@@ -1166,12 +1166,12 @@
ts? (boolean ts? (boolean
(and (and
;; Checks whether the query is a timeseries ;; Checks whether the query is a timeseries
(driver-api/match-lite (first breakout-fields) [:field _ {:temporal-unit &truthy}] true) (driver-api/match-one (first breakout-fields) [:field _ {:temporal-unit &truthy}] true)
;; (excludes x-of-y type breakouts) ;; (excludes x-of-y type breakouts)
(contains? timeseries-units (:unit (first breakout-fields))) (contains? timeseries-units (:unit (first breakout-fields)))
;; (excludes queries with LIMIT) ;; (excludes queries with LIMIT)
(nil? limit)))] (nil? limit)))]
(match [breakouts agg? ts?] (driver-api/match-one [breakouts agg? ts?]
[:none false _] ::scan [:none false _] ::scan
[:none true _] ::total [:none true _] ::total
[:one _ true] ::grouped-timeseries [:one _ true] ::grouped-timeseries

View File

@@ -1052,7 +1052,7 @@ function(bin) {
"Rename :join-alias properties fields to ::join-local. "Rename :join-alias properties fields to ::join-local.
See [[find-mapped-field-name]] for an explanation why this is done." See [[find-mapped-field-name]] for an explanation why this is done."
[expr alias] [expr alias]
(driver-api/replace-lite expr (driver-api/replace expr
[:field _ {:join-alias (a :guard (= a alias))}] [:field _ {:join-alias (a :guard (= a alias))}]
(update &match 2 set/rename-keys {:join-alias ::join-local}))) (update &match 2 set/rename-keys {:join-alias ::join-local})))
@@ -1129,7 +1129,7 @@ function(bin) {
:default (->rvalue (:default options))}}) :default (->rvalue (:default options))}})
(defn- aggregation->rvalue [ag] (defn- aggregation->rvalue [ag]
(driver-api/match-lite ag (driver-api/match-one ag
[:aggregation-options ag' _] [:aggregation-options ag' _]
(&recur ag') (&recur ag')
@@ -1469,7 +1469,7 @@ function(bin) {
(fn [m field-clause] (fn [m field-clause]
(assoc-in (assoc-in
m m
(driver-api/match-lite field-clause (driver-api/match-one field-clause
[:field (field-id :guard integer?) _] [:field (field-id :guard integer?) _]
(str/split (field-alias field-clause) #"\.") (str/split (field-alias field-clause) #"\.")
@@ -1678,7 +1678,7 @@ function(bin) {
(defn- query->collection-name (defn- query->collection-name
"Return `:collection` from a source query, if it exists." "Return `:collection` from a source query, if it exists."
[query] [query]
(driver-api/match-lite query (driver-api/match-one query
{:collection (collection :guard identity)} {:collection (collection :guard identity)}
;; ignore source queries inside `:joins` or `:collection` outside of a `:source-query` ;; ignore source queries inside `:joins` or `:collection` outside of a `:source-query`
(when (and (some #{:source-query} &parents) (when (and (some #{:source-query} &parents)
@@ -1776,7 +1776,7 @@ function(bin) {
source-alias) source-alias)
[:field source-alias opts] [:field source-alias opts]
[:field id-or-name opts])))] [:field id-or-name opts])))]
(driver-api/replace-lite form (driver-api/replace form
[:field & _] [:field & _]
(update-field-ref &match) (update-field-ref &match)

View File

@@ -16,7 +16,6 @@
[metabase.driver.util :as driver.u] [metabase.driver.util :as driver.u]
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata :as lib.metadata]
[metabase.lib.util.match :as lib.util.match]
[metabase.query-processor.compile :as qp.compile] [metabase.query-processor.compile :as qp.compile]
[metabase.query-processor.test :as qp] [metabase.query-processor.test :as qp]
[metabase.query-processor.test-util :as qp.test-util] [metabase.query-processor.test-util :as qp.test-util]
@@ -26,6 +25,7 @@
[metabase.test.data.mongo :as tdm] [metabase.test.data.mongo :as tdm]
[metabase.util.json :as json] [metabase.util.json :as json]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.match :as match]
[metabase.xrays.automagic-dashboards.core :as magic] [metabase.xrays.automagic-dashboards.core :as magic]
[taoensso.nippy :as nippy] [taoensso.nippy :as nippy]
[toucan2.core :as t2]) [toucan2.core :as t2])
@@ -674,7 +674,7 @@
(mt/test-driver :mongo (mt/test-driver :mongo
(testing "make sure x-rays don't use features that the driver doesn't support" (testing "make sure x-rays don't use features that the driver doesn't support"
(is (nil? (is (nil?
(lib.util.match/match-lite (match/match-one
(->> (magic/automagic-analysis (t2/select-one :model/Field :id (mt/id :venues :price)) {}) (->> (magic/automagic-analysis (t2/select-one :model/Field :id (mt/id :venues :price)) {})
:dashcards :dashcards
(mapcat (comp :breakout :query :dataset_query :card))) (mapcat (comp :breakout :query :dataset_query :card)))

View File

@@ -551,7 +551,7 @@
(if (contains? param :name) (if (contains? param :name)
[(:name param) (:value param)] [(:name param) (:value param)]
(when-let [field-id (driver-api/match-lite param (when-let [field-id (driver-api/match-one param
[:field (field-id :guard integer?) _] [:field (field-id :guard integer?) _]
(when (perf/some #{:dimension} &parents) (when (perf/some #{:dimension} &parents)
field-id))] field-id))]

View File

@@ -257,7 +257,7 @@
it casts to `:datetime2`." it casts to `:datetime2`."
[base-expr day-expr] [base-expr day-expr]
(if (or (= (:base-type *field-options*) :type/Date) (if (or (= (:base-type *field-options*) :type/Date)
(driver-api/match-lite base-expr [::h2x/typed _ {:database-type #{:date "date"}}] true)) (driver-api/match-one base-expr [::h2x/typed _ {:database-type #{:date "date"}}] true))
day-expr day-expr
(h2x/cast :datetime2 day-expr))) (h2x/cast :datetime2 day-expr)))
@@ -889,7 +889,7 @@
(and (has-order-by-without-limit? m) (and (has-order-by-without-limit? m)
(not (in-join-source-query? path)) (not (in-join-source-query? path))
(in-source-query? path)))] (in-source-query? path)))]
(driver-api/replace-lite inner-query (driver-api/replace inner-query
;; remove order by and then recurse in case we need to do more transformations at another level ;; remove order by and then recurse in case we need to do more transformations at another level
(m :guard (remove-order-by? &parents m)) (m :guard (remove-order-by? &parents m))
(fix-order-bys (dissoc m :order-by)) (fix-order-bys (dissoc m :order-by))

View File

@@ -12,7 +12,6 @@
If you need to use code from elsewhere, consider copying it into this namespace to minimize risk of the code If you need to use code from elsewhere, consider copying it into this namespace to minimize risk of the code
changing behaviour." changing behaviour."
(:require (:require
[clojure.core.match :refer [match]]
[clojure.edn :as edn] [clojure.edn :as edn]
[clojure.java.io :as io] [clojure.java.io :as io]
[clojure.pprint :as pprint] [clojure.pprint :as pprint]
@@ -35,6 +34,7 @@
[metabase.util.honey-sql-2 :as h2x] [metabase.util.honey-sql-2 :as h2x]
[metabase.util.json :as json] [metabase.util.json :as json]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.match :as match]
[toucan2.core :as t2] [toucan2.core :as t2]
[toucan2.execute :as t2.execute]) [toucan2.execute :as t2.execute])
(:import (:import
@@ -223,19 +223,19 @@
(defn- update-legacy-field-refs-in-viz-settings [viz-settings] (defn- update-legacy-field-refs-in-viz-settings [viz-settings]
(let [old-to-new (fn [old] (let [old-to-new (fn [old]
(match old (match/match-one old
["ref" ref] ["ref" (match ref ["ref" ref] ["ref" (match/match-one ref
["field-id" x] ["field" x nil] ["field-id" x] ["field" x nil]
["field-literal" x y] ["field" x {"base-type" y}] ["field-literal" x y] ["field" x {"base-type" y}]
["fk->" x y] (let [x (match x ["fk->" x y] (let [x (match/match-one x
[_x0 x1] x1 [_x0 x1] x1
x x) _ x)
y (match y y (match/match-one y
[_y0 y1] y1 [_y0 y1] y1
y y)] _ y)]
["field" y {:source-field x}]) ["field" y {:source-field x}])
ref ref)] _ ref)]
k k))] _ old))]
(m/update-existing viz-settings "column_settings" update-keys (m/update-existing viz-settings "column_settings" update-keys
(fn [k] (fn [k]
(-> k (-> k
@@ -269,15 +269,15 @@
(defn- update-legacy-field-refs-in-result-metadata [result-metadata] (defn- update-legacy-field-refs-in-result-metadata [result-metadata]
(let [old-to-new (fn [ref] (let [old-to-new (fn [ref]
(match ref (match/match-one ref
["field-id" x] ["field" x nil] ["field-id" x] ["field" x nil]
["field-literal" x y] ["field" x {"base-type" y}] ["field-literal" x y] ["field" x {"base-type" y}]
["fk->" x y] (let [x (match x ["fk->" x y] (let [x (match/match-one x
[_x0 x1] x1 [_x0 x1] x1
x x) _ x)
y (match y y (match/match-one y
[_y0 y1] y1 [_y0 y1] y1
y y)] _ y)]
["field" y {:source-field x}]) ["field" y {:source-field x}])
_ ref))] _ ref))]
(->> result-metadata (->> result-metadata
@@ -305,7 +305,7 @@
(defn- remove-opts (defn- remove-opts
"Removes options from the `field_ref` options map. If the resulting map is empty, it's replaced it with nil." "Removes options from the `field_ref` options map. If the resulting map is empty, it's replaced it with nil."
[field_ref & opts-to-remove] [field_ref & opts-to-remove]
(match field_ref (match/match-one field_ref
["field" id opts] ["field" id (not-empty (apply dissoc opts opts-to-remove))] ["field" id opts] ["field" id (not-empty (apply dissoc opts opts-to-remove))]
_ field_ref)) _ field_ref))
@@ -314,7 +314,7 @@
(fn [column_settings] (fn [column_settings]
(into {} (into {}
(map (fn [[k v]] (map (fn [[k v]]
(match (vec (json/decode k)) (match/match-one (vec (json/decode k))
["ref" ["field" id opts]] ["ref" ["field" id opts]]
[(json/encode ["ref" (remove-opts ["field" id opts] "join-alias")]) v] [(json/encode ["ref" (remove-opts ["field" id opts] "join-alias")]) v]
_ [k v])) _ [k v]))
@@ -333,7 +333,7 @@
(fn [column_settings] (fn [column_settings]
(into {} (into {}
(mapcat (fn [[k v]] (mapcat (fn [[k v]]
(match (vec (json/decode k)) (match/match-one (vec (json/decode k))
["ref" ["field" id opts]] ["ref" ["field" id opts]]
(for [column-metadata (column-key->metadata ["field" id opts]) (for [column-metadata (column-key->metadata ["field" id opts])
;; remove "temporal-unit" and "binning" options from the matching field refs, ;; remove "temporal-unit" and "binning" options from the matching field refs,
@@ -520,7 +520,7 @@
(fn [column_settings] (fn [column_settings]
(let [copies-with-join-alias (into {} (let [copies-with-join-alias (into {}
(mapcat (fn [[k v]] (mapcat (fn [[k v]]
(match (vec (json/decode k)) (match/match-one (vec (json/decode k))
["ref" ["field" id opts]] ["ref" ["field" id opts]]
(for [alias join-aliases] (for [alias join-aliases]
[(json/encode ["ref" ["field" id (assoc opts "join-alias" alias)]]) v]) [(json/encode ["ref" ["field" id (assoc opts "join-alias" alias)]]) v])
@@ -674,7 +674,7 @@
(fn [column_settings] (fn [column_settings]
(let [copies-with-join-alias (into {} (let [copies-with-join-alias (into {}
(mapcat (fn [[k v]] (mapcat (fn [[k v]]
(match (vec (json/decode k)) (match/match-one (vec (json/decode k))
["ref" ["field" id opts]] ["ref" ["field" id opts]]
(for [alias join-aliases] (for [alias join-aliases]
[(json/encode ["ref" ["field" id (assoc opts "join-alias" alias)]]) v]) [(json/encode ["ref" ["field" id (assoc opts "join-alias" alias)]]) v])

View File

@@ -7,9 +7,9 @@
^{:clj-kondo/ignore [:deprecated-namespace]} [metabase.driver.common.parameters :as params] ^{:clj-kondo/ignore [:deprecated-namespace]} [metabase.driver.common.parameters :as params]
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.schema.common :as lib.schema.common] [metabase.lib.schema.common :as lib.schema.common]
[metabase.lib.util.match :as lib.util.match]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
[metabase.util.malli :as mu]) [metabase.util.malli :as mu]
[metabase.util.match :as match])
(:import (:import
(metabase.driver.common.parameters Optional FunctionParam Param))) (metabase.driver.common.parameters Optional FunctionParam Param)))
@@ -23,7 +23,7 @@
(lib.schema.common/instance-of-class Optional)]) (lib.schema.common/instance-of-class Optional)])
(defn- ->param [value] (defn- ->param [value]
(lib.util.match/match-lite value (match/match-one value
(s :guard string?) (s :guard string?)
s s

View File

@@ -2,7 +2,6 @@
"The Query Processor is responsible for translating the Metabase Query Language into HoneySQL SQL forms." "The Query Processor is responsible for translating the Metabase Query Language into HoneySQL SQL forms."
(:refer-clojure :exclude [some mapv every? select-keys empty? not-empty]) (:refer-clojure :exclude [some mapv every? select-keys empty? not-empty])
(:require (:require
[clojure.core.match :refer [match]]
[clojure.string :as str] [clojure.string :as str]
[honey.sql :as sql] [honey.sql :as sql]
[honey.sql.helpers :as sql.helpers] [honey.sql.helpers :as sql.helpers]
@@ -772,28 +771,28 @@
"metabase.driver.sql.query-processor/cast-field-id-needed with a legacy (snake_cased) :model/Field" "metabase.driver.sql.query-processor/cast-field-id-needed with a legacy (snake_cased) :model/Field"
"0.48.0") "0.48.0")
(recur driver (perf/update-keys field u/->kebab-case-en) honeysql-form)) (recur driver (perf/update-keys field u/->kebab-case-en) honeysql-form))
(u/prog1 (match [base-type coercion-strategy] (u/prog1 (cond
[(:isa? :type/Number) (:isa? :Coercion/UNIXTime->Temporal)] (and (isa? base-type :type/Number) (isa? coercion-strategy :Coercion/UNIXTime->Temporal))
(unix-timestamp->honeysql driver (unix-timestamp->honeysql driver
(semantic-type->unix-timestamp-unit coercion-strategy) (semantic-type->unix-timestamp-unit coercion-strategy)
honeysql-form) honeysql-form)
[:type/Text (:isa? :Coercion/String->Temporal)] (and (= base-type :type/Text) (isa? coercion-strategy :Coercion/String->Temporal))
(cast-temporal-string driver coercion-strategy honeysql-form) (cast-temporal-string driver coercion-strategy honeysql-form)
[(:isa? :type/*) (:isa? :Coercion/Bytes->Temporal)] (and (isa? base-type :type/*) (isa? coercion-strategy :Coercion/Bytes->Temporal))
(cast-temporal-byte driver coercion-strategy honeysql-form) (cast-temporal-byte driver coercion-strategy honeysql-form)
[(:isa? :type/DateTime) (:isa? :Coercion/DateTime->Date)] (and (isa? base-type :type/DateTime) (isa? coercion-strategy :Coercion/DateTime->Date))
(->date driver honeysql-form) (->date driver honeysql-form)
[:type/Text (:isa? :Coercion/String->Float)] (and (= base-type :type/Text) (isa? coercion-strategy :Coercion/String->Float))
(->float driver honeysql-form) (->float driver honeysql-form)
[:type/Text (:isa? :Coercion/String->Integer)] (and (= base-type :type/Text) (isa? coercion-strategy :Coercion/String->Integer))
(->integer driver honeysql-form) (->integer driver honeysql-form)
[:type/Float (:isa? :Coercion/Float->Integer)] (and (= base-type :type/Float) (isa? coercion-strategy :Coercion/Float->Integer))
(->integer driver honeysql-form) (->integer driver honeysql-form)
:else honeysql-form) :else honeysql-form)
@@ -1355,7 +1354,7 @@
;; aggregation REFERENCE e.g. the ["aggregation" 0] fields we allow in order-by ;; aggregation REFERENCE e.g. the ["aggregation" 0] fields we allow in order-by
(defmethod ->honeysql [:sql :aggregation] (defmethod ->honeysql [:sql :aggregation]
[driver [_ index]] [driver [_ index]]
(driver-api/match-lite (nth (:aggregation *inner-query*) index) (driver-api/match-one (nth (:aggregation *inner-query*) index)
[:aggregation-options ag {driver-api/qp.add.desired-alias desired-alias}] [:aggregation-options ag {driver-api/qp.add.desired-alias desired-alias}]
(->honeysql driver (h2x/identifier :field-alias desired-alias)) (->honeysql driver (h2x/identifier :field-alias desired-alias))
@@ -1542,7 +1541,7 @@
([form] ([form]
(rewrite-fields-to-force-using-column-aliases form {:is-breakout false})) (rewrite-fields-to-force-using-column-aliases form {:is-breakout false}))
([form {is-breakout :is-breakout}] ([form {is-breakout :is-breakout}]
(driver-api/replace-lite (driver-api/replace
form form
[:field id-or-name opts] [:field id-or-name opts]
[:field id-or-name (cond-> opts [:field id-or-name (cond-> opts
@@ -1810,7 +1809,7 @@
;; We must not transform the head again else we'll have an infinite loop ;; We must not transform the head again else we'll have an infinite loop
;; (and we can't do it at the call-site as then it will be harder to fish out field references) ;; (and we can't do it at the call-site as then it will be harder to fish out field references)
(let [honeysql-clause (into [op] (map (partial ->honeysql driver)) args)] (let [honeysql-clause (into [op] (map (partial ->honeysql driver)) args)]
(if-let [field-arg (driver-api/match-lite args (if-let [field-arg (driver-api/match-one args
[#{:field :expression} & _] &match)] [#{:field :expression} & _] &match)]
[:or [:or
honeysql-clause honeysql-clause

View File

@@ -28,7 +28,6 @@
[metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing] [metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing]
[metabase.lib.schema.validate :as lib.schema.validate] [metabase.lib.schema.validate :as lib.schema.validate]
[metabase.lib.types.isa :as lib.types.isa] [metabase.lib.types.isa :as lib.types.isa]
[metabase.lib.util.match :as lib.util.match]
[metabase.logger.core :as logger] [metabase.logger.core :as logger]
[metabase.models.interface :as mi] [metabase.models.interface :as mi]
[metabase.premium-features.core :as premium-features] [metabase.premium-features.core :as premium-features]
@@ -56,6 +55,7 @@
[metabase.sync.util :as sync-util] [metabase.sync.util :as sync-util]
[metabase.system.core :as system] [metabase.system.core :as system]
[metabase.upload.db :as upload.db] [metabase.upload.db :as upload.db]
[metabase.util.match :as match]
[metabase.warehouse-schema.models.table :as table] [metabase.warehouse-schema.models.table :as table]
[potemkin :as p])) [potemkin :as p]))
@@ -102,12 +102,9 @@
lib.schema.common/instance-of-class lib.schema.common/instance-of-class
lib.schema.temporal-bucketing/date-bucketing-units lib.schema.temporal-bucketing/date-bucketing-units
lib.types.isa/temporal? lib.types.isa/temporal?
lib.util.match/match match/match-one
lib.util.match/match-one match/match-many
lib.util.match/match-lite match/replace
lib.util.match/match-many
lib.util.match/replace
lib.util.match/replace-lite
lib/truncate-alias lib/truncate-alias
lib/->legacy-MBQL lib/->legacy-MBQL
lib/->metadata-provider lib/->metadata-provider

View File

@@ -4,13 +4,13 @@
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
(:require (:require
[metabase.lib.filter.desugar.jvm :as lib.filter.desugar.jvm] [metabase.lib.filter.desugar.jvm :as lib.filter.desugar.jvm]
[metabase.lib.util.match :as lib.util.match])) [metabase.util.match :as match]))
(defn desugar-host-and-domain (defn desugar-host-and-domain
"Unwrap host and domain." "Unwrap host and domain."
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[expression] [expression]
(lib.util.match/replace-lite (match/replace
expression expression
[:host column] [:host column]
(&recur [:regex-match-first column (str lib.filter.desugar.jvm/host-regex)]) (&recur [:regex-match-first column (str lib.filter.desugar.jvm/host-regex)])

View File

@@ -38,10 +38,10 @@
[metabase.lib.schema.settings :as lib.schema.settings] [metabase.lib.schema.settings :as lib.schema.settings]
[metabase.lib.schema.template-tag :as lib.schema.template-tag] [metabase.lib.schema.template-tag :as lib.schema.template-tag]
[metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing] [metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing]
[metabase.lib.util.match :as lib.util.match]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [every? select-keys #?(:clj doseq) some mapv update-keys empty? not-empty]] [metabase.util.performance :refer [every? select-keys #?(:clj doseq) some mapv update-keys empty? not-empty]]
[metabase.util.time :as u.time])) [metabase.util.time :as u.time]))
@@ -947,7 +947,7 @@
(defn- replace-exclude-date-filters (defn- replace-exclude-date-filters
"Replaces legacy exclude date filter clauses that rely on temporal bucketing with `:temporal-extract` function calls." "Replaces legacy exclude date filter clauses that rely on temporal bucketing with `:temporal-extract` function calls."
[filter-clause] [filter-clause]
(lib.util.match/replace-lite filter-clause (match/replace filter-clause
[:!= [:!=
[:field id-or-name (opts :guard (= (:temporal-unit opts) :hour-of-day))] [:field id-or-name (opts :guard (= (:temporal-unit opts) :hour-of-day))]
& (args :guard (every? number? args))] & (args :guard (every? number? args))]
@@ -1011,7 +1011,7 @@
expression and convert it to a `:relative-time-interval` call, honoring the original user intent. See #46211 and expression and convert it to a `:relative-time-interval` call, honoring the original user intent. See #46211 and
#46438 for details." #46438 for details."
[clause] [clause]
(lib.util.match/replace-lite clause (match/replace clause
[:between [:between
[:+ [:+
field field

View File

@@ -13,11 +13,11 @@
[metabase.lib.schema.common :as lib.schema.common] [metabase.lib.schema.common :as lib.schema.common]
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.schema.ref :as lib.schema.ref] [metabase.lib.schema.ref :as lib.schema.ref]
[metabase.lib.util.match :as lib.util.match]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.i18n :as i18n] [metabase.util.i18n :as i18n]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.performance :refer [some mapv every? not-empty get-in #?(:clj for)]] [metabase.util.performance :refer [some mapv every? not-empty get-in #?(:clj for)]]
[metabase.util.time :as u.time])) [metabase.util.time :as u.time]))
@@ -84,7 +84,7 @@
(defn- combine-compound-filters-of-type (defn- combine-compound-filters-of-type
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[compound-type subclauses] [compound-type subclauses]
(mapcat #(lib.util.match/match-lite % (mapcat #(match/match-one %
[#{compound-type} & args] args [#{compound-type} & args] args
_ [%]) _ [%])
subclauses)) subclauses))
@@ -160,7 +160,7 @@
"Rewrite `:inside` filter clauses as a pair of `:between` clauses." "Rewrite `:inside` filter clauses as a pair of `:between` clauses."
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[m] [m]
(lib.util.match/replace-lite m (match/replace m
[:inside lat-field lon-field lat-max lon-min lat-min lon-max] [:inside lat-field lon-field lat-max lon-min lat-min lon-max]
[:and [:and
[:between lat-field lat-min lat-max] [:between lat-field lat-min lat-max]
@@ -170,7 +170,7 @@
"Rewrite `:is-null` and `:not-null` filter clauses as simpler `:=` and `:!=`, respectively." "Rewrite `:is-null` and `:not-null` filter clauses as simpler `:=` and `:!=`, respectively."
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[m] [m]
(lib.util.match/replace-lite m (match/replace m
[:is-null field] [:= field nil] [:is-null field] [:= field nil]
[:not-null field] [:!= field nil])) [:not-null field] [:!= field nil]))
@@ -194,7 +194,7 @@
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[m] [m]
#_{:clj-kondo/ignore [:deprecated-var]} #_{:clj-kondo/ignore [:deprecated-var]}
(lib.util.match/replace-lite m (match/replace m
[:is-empty clause] [:is-empty clause]
(if (emptyable? clause) (if (emptyable? clause)
[:or [:= clause nil] [:= clause ""]] [:or [:= clause nil] [:= clause ""]]
@@ -210,7 +210,7 @@
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[m unit] [m unit]
#_{:clj-kondo/ignore [:deprecated-var]} #_{:clj-kondo/ignore [:deprecated-var]}
(lib.util.match/replace-lite m (match/replace m
[:field id-or-name opts] [:field id-or-name opts]
[:field id-or-name (assoc opts :temporal-unit unit)] [:field id-or-name (assoc opts :temporal-unit unit)]
@@ -223,7 +223,7 @@
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[m] [m]
#_{:clj-kondo/ignore [:deprecated-var]} #_{:clj-kondo/ignore [:deprecated-var]}
(lib.util.match/replace-lite m (match/replace m
[:time-interval field-or-expression n unit] (&recur [:time-interval field-or-expression n unit nil]) [:time-interval field-or-expression n unit] (&recur [:time-interval field-or-expression n unit nil])
;; replace current/last/next with corresponding value of n and recur ;; replace current/last/next with corresponding value of n and recur
@@ -275,7 +275,7 @@
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[m] [m]
#_{:clj-kondo/ignore [:deprecated-var]} #_{:clj-kondo/ignore [:deprecated-var]}
(lib.util.match/replace-lite (match/replace
m m
[:relative-time-interval col value bucket offset-value offset-bucket] [:relative-time-interval col value bucket offset-value offset-bucket]
(let [col-default-bucket (cond-> col (and (vector? col) (= 3 (count col))) (let [col-default-bucket (cond-> col (and (vector? col) (= 3 (count col)))
@@ -298,7 +298,7 @@
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[m] [m]
#_{:clj-kondo/ignore [:deprecated-var]} #_{:clj-kondo/ignore [:deprecated-var]}
(lib.util.match/replace-lite (match/replace
m m
[:during col value unit] [:during col value unit]
(let [col-default-bucket (cond-> col (let [col-default-bucket (cond-> col
@@ -315,7 +315,7 @@
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[m] [m]
#_{:clj-kondo/ignore [:deprecated-var]} #_{:clj-kondo/ignore [:deprecated-var]}
(lib.util.match/replace-lite m (match/replace m
[:if & args] [:if & args]
(into [:case] args))) (into [:case] args)))
@@ -324,7 +324,7 @@
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[m] [m]
#_{:clj-kondo/ignore [:deprecated-var]} #_{:clj-kondo/ignore [:deprecated-var]}
(lib.util.match/replace-lite m (match/replace m
[:in & args] [:in & args]
(into [:=] args) (into [:=] args)
@@ -340,7 +340,7 @@
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[m] [m]
#_{:clj-kondo/ignore [:deprecated-var]} #_{:clj-kondo/ignore [:deprecated-var]}
(lib.util.match/replace-lite m (match/replace m
[:does-not-contain & args] [:does-not-contain & args]
[:not (into [:contains] args)])) [:not (into [:contains] args)]))
@@ -359,7 +359,7 @@
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[m] [m]
#_{:clj-kondo/ignore [:deprecated-var]} #_{:clj-kondo/ignore [:deprecated-var]}
(lib.util.match/replace-lite m (match/replace m
[:= field x y & more] [:= field x y & more]
(apply vector :or (for [x (concat [x y] more)] (apply vector :or (for [x (concat [x y] more)]
[:= field x])) [:= field x]))
@@ -383,12 +383,12 @@
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[m] [m]
#_{:clj-kondo/ignore [:deprecated-var]} #_{:clj-kondo/ignore [:deprecated-var]}
(lib.util.match/replace-lite m (match/replace m
[clause field & (args :guard (some #{[:relative-datetime :current]} args))] [clause field & (args :guard (some #{[:relative-datetime :current]} args))]
(let [temporal-unit (or (lib.util.match/match-lite field (let [temporal-unit (or (match/match-one field
[:field _ {:temporal-unit temporal-unit}] temporal-unit) [:field _ {:temporal-unit temporal-unit}] temporal-unit)
:default)] :default)]
(into [clause field] (lib.util.match/replace-lite args (into [clause field] (match/replace args
[:relative-datetime :current] [:relative-datetime :current]
[:relative-datetime 0 temporal-unit]))))) [:relative-datetime 0 temporal-unit])))))
@@ -420,14 +420,14 @@
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[m] [m]
#_{:clj-kondo/ignore [:deprecated-var]} #_{:clj-kondo/ignore [:deprecated-var]}
(lib.util.match/replace-lite m (match/replace m
[(op :guard temporal-extract-ops) field & args] [(op :guard temporal-extract-ops) field & args]
[:temporal-extract field (temporal-extract-ops->unit [op (first args)])])) [:temporal-extract field (temporal-extract-ops->unit [op (first args)])]))
(defn- desugar-divide-with-extra-args (defn- desugar-divide-with-extra-args
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[expression] [expression]
(lib.util.match/replace-lite expression (match/replace expression
[:/ x y z & more] [:/ x y z & more]
(&recur (into [:/ [:/ x y]] (cons z more))))) (&recur (into [:/ [:/ x y]] (cons z more)))))
@@ -451,7 +451,7 @@
{:deprecated "0.57.0"} {:deprecated "0.57.0"}
[expression] [expression]
#_{:clj-kondo/ignore [:deprecated-var]} #_{:clj-kondo/ignore [:deprecated-var]}
(lib.util.match/replace-lite expression (match/replace expression
[:month-name column] [:month-name column]
(&recur (temporal-case-expression column :month-of-year 12)) (&recur (temporal-case-expression column :month-of-year 12))
[:quarter-name column] [:quarter-name column]
@@ -721,7 +721,7 @@
(not-empty (not-empty
(into #{} (into #{}
(comp cat (filter some?)) (comp cat (filter some?))
(lib.util.match/match-many coll (match/match-many coll
[:field (id :guard integer?) opts] [:field (id :guard integer?) opts]
[id (:source-field opts)])))) [id (:source-field opts)]))))

View File

@@ -23,13 +23,13 @@
[metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing] [metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing]
[metabase.lib.temporal-bucket :as lib.temporal-bucket] [metabase.lib.temporal-bucket :as lib.temporal-bucket]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.types.core :as types] [metabase.types.core :as types]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.i18n :as i18n] [metabase.util.i18n :as i18n]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.number :as u.number] [metabase.util.number :as u.number]
[metabase.util.performance :refer [mapv some select-keys not-empty get-in every? #?(:clj doseq) #?(:clj for)]])) [metabase.util.performance :refer [mapv some select-keys not-empty get-in every? #?(:clj doseq) #?(:clj for)]]))
@@ -578,7 +578,7 @@
(defn- referred-expressions (defn- referred-expressions
[expr] [expr]
(set (lib.util.match/match-many expr [:expression _opts x & _] x))) (set (match/match-many expr [:expression _opts x & _] x)))
(defn- aggregation->name (defn- aggregation->name
[query stage-number aggregation] [query stage-number aggregation]
@@ -586,7 +586,7 @@
(defn- referred-aggregations (defn- referred-aggregations
[agg] [agg]
(set (lib.util.match/match-many agg [:aggregation _opts x & _] x))) (set (match/match-many agg [:aggregation _opts x & _] x)))
(defn- cyclic-definition (defn- cyclic-definition
([node->children] ([node->children]
@@ -756,11 +756,11 @@
(-> nested name u/->camelCaseEn u/capitalize-first-char))) (-> nested name u/->camelCaseEn u/capitalize-first-char)))
:friendly true}) :friendly true})
(when (and (= expression-mode :expression) (when (and (= expression-mode :expression)
(lib.util.match/match-lite expr :offset true)) (match/match-one expr :offset true))
{:message (i18n/tru "OFFSET is not supported in custom columns") {:message (i18n/tru "OFFSET is not supported in custom columns")
:friendly true}) :friendly true})
(when (and (= expression-mode :filter) (when (and (= expression-mode :filter)
(lib.util.match/match-lite expr :offset true)) (match/match-one expr :offset true))
{:message (i18n/tru "OFFSET is not supported in custom filters") {:message (i18n/tru "OFFSET is not supported in custom filters")
:friendly true}) :friendly true})
(when (and (lib.schema.common/is-clause? :value expr) (when (and (lib.schema.common/is-clause? :value expr)

View File

@@ -26,11 +26,11 @@
[metabase.lib.temporal-bucket :as lib.temporal-bucket] [metabase.lib.temporal-bucket :as lib.temporal-bucket]
[metabase.lib.types.isa :as lib.types.isa] [metabase.lib.types.isa :as lib.types.isa]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.formatting.date :as fmt.date] [metabase.util.formatting.date :as fmt.date]
[metabase.util.i18n :as i18n] [metabase.util.i18n :as i18n]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.number :as u.number] [metabase.util.number :as u.number]
[metabase.util.performance :refer [every? mapv select-keys some #?(:clj doseq) #?(:clj for)]] [metabase.util.performance :refer [every? mapv select-keys some #?(:clj doseq) #?(:clj for)]]
[metabase.util.time :as u.time])) [metabase.util.time :as u.time]))
@@ -313,7 +313,7 @@
string-col? #(ref-clause-with-type? % [:type/Text :type/TextLike]) string-col? #(ref-clause-with-type? % [:type/Text :type/TextLike])
result (fn [op col-ref args options] result (fn [op col-ref args options]
{:operator op, :column (ref->col col-ref), :values (vec args), :options options})] {:operator op, :column (ref->col col-ref), :values (vec args), :options options})]
(lib.util.match/match-lite filter-clause (match/match-one filter-clause
;; no arguments ;; no arguments
[(op :guard #{:is-empty :not-empty}) _ (col-ref :guard string-col?) & (args :len 0 :guard (every? string? args))] [(op :guard #{:is-empty :not-empty}) _ (col-ref :guard string-col?) & (args :len 0 :guard (every? string? args))]
(result op col-ref [] {}) (result op col-ref [] {})
@@ -344,7 +344,7 @@
(defn- expression-arg->number (defn- expression-arg->number
[arg] [arg]
(lib.util.match/match-lite arg (match/match-one arg
(value :guard number?) (value :guard number?)
value value
@@ -376,7 +376,7 @@
(let [ref->col #(column-metadata-from-ref query stage-number %) (let [ref->col #(column-metadata-from-ref query stage-number %)
number-col? #(ref-clause-with-type? % [:type/Number]) number-col? #(ref-clause-with-type? % [:type/Number])
number-arg? #(some? (expression-arg->number %))] number-arg? #(some? (expression-arg->number %))]
(lib.util.match/match-lite filter-clause (match/match-one filter-clause
(:or (:or
;; no arguments ;; no arguments
[(op :guard #{:is-null :not-null}) _ (col-ref :guard number-col?) & (args :len 0 :guard (every? number-arg? args))] [(op :guard #{:is-null :not-null}) _ (col-ref :guard number-col?) & (args :len 0 :guard (every? number-arg? args))]
@@ -433,7 +433,7 @@
:column (ref->col col-ref) :column (ref->col col-ref)
:values (mapv expression-arg->number args)} :values (mapv expression-arg->number args)}
lon-col-ref (assoc :longitude-column (ref->col lon-col-ref))))] lon-col-ref (assoc :longitude-column (ref->col lon-col-ref))))]
(lib.util.match/match-lite filter-clause (match/match-one filter-clause
(:or (:or
;; multiple arguments, `:=` ;; multiple arguments, `:=`
[(op :guard #{:= :in}) _ (col-ref :guard coordinate-col?) & (args :guard (every? number-arg? args))] [(op :guard #{:= :in}) _ (col-ref :guard coordinate-col?) & (args :guard (every? number-arg? args))]
@@ -478,7 +478,7 @@
filter-clause :- ::lib.schema.expression/expression] filter-clause :- ::lib.schema.expression/expression]
(let [ref->col #(column-metadata-from-ref query stage-number %) (let [ref->col #(column-metadata-from-ref query stage-number %)
boolean-col? #(ref-clause-with-type? % [:type/Boolean])] boolean-col? #(ref-clause-with-type? % [:type/Boolean])]
(lib.util.match/match-lite filter-clause (match/match-one filter-clause
(:or (:or
;; no arguments ;; no arguments
[(op :guard #{:is-null :not-null}) _ (col-ref :guard boolean-col?) & (args :len 0 :guard (every? boolean? args))] [(op :guard #{:is-null :not-null}) _ (col-ref :guard boolean-col?) & (args :len 0 :guard (every? boolean? args))]
@@ -521,7 +521,7 @@
values (mapv u.time/coerce-to-timestamp args)] values (mapv u.time/coerce-to-timestamp args)]
(when (every? u.time/valid? values) (when (every? u.time/valid? values)
{:operator op, :column (ref->col col-ref), :values values, :with-time? (not date?)})))] {:operator op, :column (ref->col col-ref), :values values, :with-time? (not date?)})))]
(lib.util.match/match-lite filter-clause (match/match-one filter-clause
(:or (:or
;; exactly 1 argument ;; exactly 1 argument
[(op :guard #{:= :> :<}) _ (col-ref :guard date-col?) & (args :len 1 :guard (every? string? args))] [(op :guard #{:= :> :<}) _ (col-ref :guard date-col?) & (args :len 1 :guard (every? string? args))]
@@ -564,7 +564,7 @@
filter-clause :- ::lib.schema.expression/expression] filter-clause :- ::lib.schema.expression/expression]
(let [ref->col #(column-metadata-from-ref query stage-number %) (let [ref->col #(column-metadata-from-ref query stage-number %)
date-col? #(ref-clause-with-type? % [:type/Date :type/DateTime])] date-col? #(ref-clause-with-type? % [:type/Date :type/DateTime])]
(lib.util.match/match-lite filter-clause (match/match-one filter-clause
[:time-interval [:time-interval
opts opts
(col-ref :guard date-col?) (col-ref :guard date-col?)
@@ -634,7 +634,7 @@
op->unit {:get-hour :hour-of-day op->unit {:get-hour :hour-of-day
:get-month :month-of-year :get-month :month-of-year
:get-quarter :quarter-of-year}] :get-quarter :quarter-of-year}]
(lib.util.match/match-lite filter-clause (match/match-one filter-clause
;; no arguments ;; no arguments
[(op :guard #{:is-null :not-null}) _ (col-ref :guard date-col?) & (args :len 0 :guard (every? int? args))] [(op :guard #{:is-null :not-null}) _ (col-ref :guard date-col?) & (args :len 0 :guard (every? int? args))]
{:operator op, :column (ref->col col-ref), :values []} {:operator op, :column (ref->col col-ref), :values []}
@@ -673,7 +673,7 @@
filter-clause :- ::lib.schema.expression/expression] filter-clause :- ::lib.schema.expression/expression]
(let [ref->col #(column-metadata-from-ref query stage-number %) (let [ref->col #(column-metadata-from-ref query stage-number %)
time-col? #(ref-clause-with-type? % [:type/Time])] time-col? #(ref-clause-with-type? % [:type/Time])]
(lib.util.match/match-lite filter-clause (match/match-one filter-clause
(:or (:or
;; no arguments ;; no arguments
[(op :guard #{:is-null :not-null}) _ (col-ref :guard time-col?) & (args :len 0 :guard (every? string? args))] [(op :guard #{:is-null :not-null}) _ (col-ref :guard time-col?) & (args :len 0 :guard (every? string? args))]
@@ -711,7 +711,7 @@
supported-col? #(and (lib.util/ref-clause? %) supported-col? #(and (lib.util/ref-clause? %)
(not (lib.util/original-isa? % :type/Text)) (not (lib.util/original-isa? % :type/Text))
(not (lib.util/original-isa? % :type/TextLike)))] (not (lib.util/original-isa? % :type/TextLike)))]
(lib.util.match/match-lite filter-clause (match/match-one filter-clause
[(op :guard #{:is-null :not-null}) _ (col-ref :guard supported-col?)] [(op :guard #{:is-null :not-null}) _ (col-ref :guard supported-col?)]
{:operator op, :column (ref->col col-ref)} {:operator op, :column (ref->col col-ref)}
@@ -734,7 +734,7 @@
(mu/defn join-condition-parts :- [:maybe JoinConditionParts] (mu/defn join-condition-parts :- [:maybe JoinConditionParts]
"Destructures a join condition created by [[join-condition-clause]]." "Destructures a join condition created by [[join-condition-clause]]."
[join-condition :- ::lib.schema.join/condition] [join-condition :- ::lib.schema.join/condition]
(lib.util.match/match-lite join-condition (match/match-one join-condition
[(op :guard lib.schema.join/condition-operators) _ lhs rhs] [(op :guard lib.schema.join/condition-operators) _ lhs rhs]
{:operator op, :lhs-expression lhs, :rhs-expression rhs} {:operator op, :lhs-expression lhs, :rhs-expression rhs}
@@ -772,7 +772,7 @@
->unit {:get-hour :hour-of-day ->unit {:get-hour :hour-of-day
:get-month :month-of-year :get-month :month-of-year
:get-quarter :quarter-of-year}] :get-quarter :quarter-of-year}]
(lib.util.match/match-lite filter-clause (match/match-one filter-clause
[#{:= :in} _ [:get-day-of-week _ (_ :guard temporal?) :iso] (b :guard int?)] [#{:= :in} _ [:get-day-of-week _ (_ :guard temporal?) :iso] (b :guard int?)]
(inflections/plural (u.time/format-unit b :day-of-week-iso)) (inflections/plural (u.time/format-unit b :day-of-week-iso))

View File

@@ -21,11 +21,11 @@
[metabase.lib.temporal-bucket :as lib.temporal-bucket] [metabase.lib.temporal-bucket :as lib.temporal-bucket]
[metabase.lib.types.isa :as lib.types.isa] [metabase.lib.types.isa :as lib.types.isa]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.i18n :as i18n] [metabase.util.i18n :as i18n]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.number :as u.number] [metabase.util.number :as u.number]
[metabase.util.performance :as perf :refer [every? some mapv empty? #?(:clj doseq) #?(:clj for)]] [metabase.util.performance :as perf :refer [every? some mapv empty? #?(:clj doseq) #?(:clj for)]]
[metabase.util.time :as u.time])) [metabase.util.time :as u.time]))
@@ -130,7 +130,7 @@
:get-month :month-of-year :get-month :month-of-year
:get-quarter :quarter-of-year :get-quarter :quarter-of-year
:get-year :year-of-era}] :get-year :year-of-era}]
(lib.util.match/match-lite expr (match/match-one expr
[(op :guard #{:= :in :!= :not-in}) _ [:get-hour _ (a :guard temporal?)] (b :guard int?)] [(op :guard #{:= :in :!= :not-in}) _ [:get-hour _ (a :guard temporal?)] (b :guard int?)]
(i18n/tru "{0} {1} {2}" (->unbucketed-display-name a) (if (#{:= :in} op) "is at" "excludes the hour of") (i18n/tru "{0} {1} {2}" (->unbucketed-display-name a) (if (#{:= :in} op) "is at" "excludes the hour of")
(u.time/format-unit b :hour-of-day)) (u.time/format-unit b :hour-of-day))
@@ -238,7 +238,7 @@
(let [->display-name #(lib.metadata.calculation/display-name query stage-number % style) (let [->display-name #(lib.metadata.calculation/display-name query stage-number % style)
->temporal-name #(u.time/format-unit % nil) ->temporal-name #(u.time/format-unit % nil)
temporal? #(lib.util/original-isa? % :type/Temporal)] temporal? #(lib.util/original-isa? % :type/Temporal)]
(lib.util.match/match-lite expr (match/match-one expr
[:< _ (x :guard temporal?) (y :guard string?)] [:< _ (x :guard temporal?) (y :guard string?)]
((binary-filter-display-fns :is-before) (->display-name x) (->temporal-name y)) ((binary-filter-display-fns :is-before) (->display-name x) (->temporal-name y))
@@ -266,7 +266,7 @@
->unbucketed-display-name #(-> % ->unbucketed-display-name #(-> %
(update 1 dissoc :temporal-unit) (update 1 dissoc :temporal-unit)
->display-name)] ->display-name)]
(lib.util.match/match-lite expr (match/match-one expr
[:between _ x (y :guard string?) (z :guard string?)] [:between _ x (y :guard string?) (z :guard string?)]
((binary-filter-display-fns :is) ((binary-filter-display-fns :is)
(->unbucketed-display-name x) (->unbucketed-display-name x)

View File

@@ -13,9 +13,9 @@
[metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing] [metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing]
[metabase.lib.schema.util :as lib.schema.util] [metabase.lib.schema.util :as lib.schema.util]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [some mapv]] [metabase.util.performance :refer [some mapv]]
[metabase.util.time :as u.time])) [metabase.util.time :as u.time]))
@@ -30,7 +30,7 @@
(mu/defn- desugar-inside :- ::clause (mu/defn- desugar-inside :- ::clause
"Rewrite `:inside` filter clauses as a pair of `:between` clauses." "Rewrite `:inside` filter clauses as a pair of `:between` clauses."
[expr :- ::clause] [expr :- ::clause]
(lib.util.match/replace-lite expr (match/replace expr
[:inside opts lat-field lon-field lat-max lon-min lat-min lon-max] [:inside opts lat-field lon-field lat-max lon-min lat-min lon-max]
(-> (lib.filter/and (-> (lib.filter/and
(lib.filter/between lat-field lat-min lat-max) (lib.filter/between lat-field lat-min lat-max)
@@ -40,7 +40,7 @@
(mu/defn- desugar-is-null-and-not-null :- ::clause (mu/defn- desugar-is-null-and-not-null :- ::clause
"Rewrite `:is-null` and `:not-null` filter clauses as simpler `:=` and `:!=`, respectively." "Rewrite `:is-null` and `:not-null` filter clauses as simpler `:=` and `:!=`, respectively."
[expr :- ::clause] [expr :- ::clause]
(lib.util.match/replace-lite expr (match/replace expr
[:is-null opts x] [:= opts x nil] [:is-null opts x] [:= opts x nil]
[:not-null opts x] [:!= opts x nil])) [:not-null opts x] [:!= opts x nil]))
@@ -54,7 +54,7 @@
If `:not-empty` is called on `::lib.schema.expression/emptyable` type, expand check for empty string. For If `:not-empty` is called on `::lib.schema.expression/emptyable` type, expand check for empty string. For
non-`emptyable` types act as `:is-null`. If field has nil base type it is considered not emptyable expansion wise." non-`emptyable` types act as `:is-null`. If field has nil base type it is considered not emptyable expansion wise."
[expr :- ::clause] [expr :- ::clause]
(lib.util.match/replace-lite expr (match/replace expr
[:is-empty opts arg] [:is-empty opts arg]
(-> (if (emptyable? arg) (-> (if (emptyable? arg)
(lib.filter/or (lib.filter/or
@@ -75,14 +75,14 @@
"Replace a field or expression inside :time-interval" "Replace a field or expression inside :time-interval"
[expr :- ::clause [expr :- ::clause
unit :- ::lib.schema.temporal-bucketing/unit] unit :- ::lib.schema.temporal-bucketing/unit]
(lib.util.match/replace-lite expr (match/replace expr
[#{:field :expression} & _] [#{:field :expression} & _]
(lib.options/update-options &match assoc :temporal-unit unit))) (lib.options/update-options &match assoc :temporal-unit unit)))
(mu/defn- desugar-time-interval :- ::clause (mu/defn- desugar-time-interval :- ::clause
"Rewrite `:time-interval` filter clauses as simpler ones like `:=` or `:between`." "Rewrite `:time-interval` filter clauses as simpler ones like `:=` or `:between`."
[expr :- ::clause] [expr :- ::clause]
(lib.util.match/replace-lite expr (match/replace expr
;; replace current/last/next with corresponding value of n and recur ;; replace current/last/next with corresponding value of n and recur
[:time-interval opts field-or-expression :current unit] (&recur [:time-interval opts field-or-expression 0 unit]) [:time-interval opts field-or-expression :current unit] (&recur [:time-interval opts field-or-expression 0 unit])
[:time-interval opts field-or-expression :last unit] (&recur [:time-interval opts field-or-expression -1 unit]) [:time-interval opts field-or-expression :last unit] (&recur [:time-interval opts field-or-expression -1 unit])
@@ -127,7 +127,7 @@
(mu/defn- desugar-relative-time-interval :- ::clause (mu/defn- desugar-relative-time-interval :- ::clause
"Transform `:relative-time-interval` to `:and` expression." "Transform `:relative-time-interval` to `:and` expression."
[expr :- ::clause] [expr :- ::clause]
(lib.util.match/replace-lite expr (match/replace expr
[:relative-time-interval opts arg (value :guard int?) bucket offset-value offset-bucket] [:relative-time-interval opts arg (value :guard int?) bucket offset-value offset-bucket]
(let [bucket #(lib.util/fresh-uuids bucket) (let [bucket #(lib.util/fresh-uuids bucket)
offset-value #(lib.util/fresh-uuids offset-value) offset-value #(lib.util/fresh-uuids offset-value)
@@ -149,7 +149,7 @@
(mu/defn- desugar-during :- ::clause (mu/defn- desugar-during :- ::clause
"Transform a `:during` expression to an `:and` expression." "Transform a `:during` expression to an `:and` expression."
[expr :- ::clause] [expr :- ::clause]
(lib.util.match/replace-lite expr (match/replace expr
[:during opts arg value unit] [:during opts arg value unit]
(let [lower-bound (u.time/truncate value unit) (let [lower-bound (u.time/truncate value unit)
upper-bound (u.time/add lower-bound unit 1)] upper-bound (u.time/add lower-bound unit 1)]
@@ -166,7 +166,7 @@
(lib.util/clause? fragment) (desugar-if fragment) (lib.util/clause? fragment) (desugar-if fragment)
(vector? fragment) (mapv process-fragment fragment) (vector? fragment) (mapv process-fragment fragment)
:else fragment))] :else fragment))]
(lib.util.match/replace-lite expr (match/replace expr
[:if opts & args] [:if opts & args]
(-> (map process-fragment args) (-> (map process-fragment args)
(->> (apply lib.expression/case)) (->> (apply lib.expression/case))
@@ -175,7 +175,7 @@
(mu/defn- desugar-in :- ::clause (mu/defn- desugar-in :- ::clause
"Transform `:in` and `:not-in` expressions to `:=` and `:!=` expressions." "Transform `:in` and `:not-in` expressions to `:=` and `:!=` expressions."
[expr :- ::clause] [expr :- ::clause]
(lib.util.match/replace-lite expr (match/replace expr
[:in opts & args] [:in opts & args]
(-> (apply lib.filter/= args) (-> (apply lib.filter/= args)
(merge-options opts)) (merge-options opts))
@@ -191,7 +191,7 @@
several `[:and [:does-not-contain ...] [:does-not-contain ...] ...]` clauses, which then get rewritten here into several `[:and [:does-not-contain ...] [:does-not-contain ...] ...]` clauses, which then get rewritten here into
`[:and [:not [:contains ...]] [:not [:contains ...]]]`." `[:and [:not [:contains ...]] [:not [:contains ...]]]`."
[expr :- ::clause] [expr :- ::clause]
(lib.util.match/replace-lite expr (match/replace expr
[:does-not-contain opts whole & parts] [:does-not-contain opts whole & parts]
(-> (lib.filter/not (-> (apply lib.filter/contains whole parts) (-> (lib.filter/not (-> (apply lib.filter/contains whole parts)
;; need to preserve stuff like `:case-sensitive`. Prefer values in each clause over `opts` ;; need to preserve stuff like `:case-sensitive`. Prefer values in each clause over `opts`
@@ -212,7 +212,7 @@
Multi-argument forms use MBQL 5 style with the options at index 1, **even if there are no options**: Multi-argument forms use MBQL 5 style with the options at index 1, **even if there are no options**:
`[:contains {} field x y z]`." `[:contains {} field x y z]`."
[expr :- ::clause] [expr :- ::clause]
(lib.util.match/replace-lite expr (match/replace expr
[:= opts field a b & more] [:= opts field a b & more]
(-> (apply lib.filter/or (map (fn [expr] (-> (apply lib.filter/or (map (fn [expr]
(lib.filter/= (lib.util/fresh-uuids field) expr)) (lib.filter/= (lib.util/fresh-uuids field) expr))
@@ -244,17 +244,17 @@
"Replace `relative-datetime` clauses like `[:relative-datetime :current]` with `[:relative-datetime 0 <unit>]`. "Replace `relative-datetime` clauses like `[:relative-datetime :current]` with `[:relative-datetime 0 <unit>]`.
`<unit>` is inferred from the `:field` the clause is being compared to (if any), otherwise falls back to `default.`" `<unit>` is inferred from the `:field` the clause is being compared to (if any), otherwise falls back to `default.`"
[expr :- ::clause] [expr :- ::clause]
(lib.util.match/replace-lite expr (match/replace expr
[tag opts field & (args :guard (some (fn [arg] [tag opts field & (args :guard (some (fn [arg]
(lib.util.match/match-lite arg [:relative-datetime _opts :current] true)) (match/match-one arg [:relative-datetime _opts :current] true))
args))] args))]
(let [temporal-unit (or (lib.util.match/match-lite field (let [temporal-unit (or (match/match-one field
[:field {:temporal-unit temporal-unit} _] [:field {:temporal-unit temporal-unit} _]
temporal-unit) temporal-unit)
:default)] :default)]
(into [tag opts field] (into [tag opts field]
(map (fn [arg] (map (fn [arg]
(lib.util.match/replace-lite arg (match/replace arg
[:relative-datetime relative-datetime-opts :current] [:relative-datetime relative-datetime-opts :current]
(-> (lib.expression/relative-datetime 0 temporal-unit) (-> (lib.expression/relative-datetime 0 temporal-unit)
(merge-options relative-datetime-opts))))) (merge-options relative-datetime-opts)))))
@@ -285,13 +285,13 @@
(mu/defn- desugar-temporal-extract :- ::clause (mu/defn- desugar-temporal-extract :- ::clause
"Replace datetime extractions clauses like `[:get-year field]` with `[:temporal-extract field :year]`." "Replace datetime extractions clauses like `[:get-year field]` with `[:temporal-extract field :year]`."
[expr :- ::clause] [expr :- ::clause]
(lib.util.match/replace-lite expr (match/replace expr
[(tag :guard temporal-extract-ops) opts field & args] [(tag :guard temporal-extract-ops) opts field & args]
[:temporal-extract opts field (temporal-extract-ops->unit [tag (first args)])])) [:temporal-extract opts field (temporal-extract-ops->unit [tag (first args)])]))
(mu/defn- desugar-divide-with-extra-args :- ::clause (mu/defn- desugar-divide-with-extra-args :- ::clause
[expression :- ::clause] [expression :- ::clause]
(lib.util.match/replace-lite expression (match/replace expression
[:/ opts x y z & more] [:/ opts x y z & more]
(&recur (-> (apply lib.expression// (lib.expression// x y) z more) (&recur (-> (apply lib.expression// (lib.expression// x y) z more)
(merge-options opts))))) (merge-options opts)))))
@@ -318,7 +318,7 @@
Uses the user's locale rather than the site locale, so the results will depend on the runner of the query, not just Uses the user's locale rather than the site locale, so the results will depend on the runner of the query, not just
the query itself. Filtering should be done based on the number, rather than the name." the query itself. Filtering should be done based on the number, rather than the name."
[expression :- ::clause] [expression :- ::clause]
(lib.util.match/replace-lite expression (match/replace expression
[:month-name opts expr] (&recur (temporal-case-expression expr opts :month-of-year 12)) [:month-name opts expr] (&recur (temporal-case-expression expr opts :month-of-year 12))
[:quarter-name opts expr] (&recur (temporal-case-expression expr opts :quarter-of-year 4)) [:quarter-name opts expr] (&recur (temporal-case-expression expr opts :quarter-of-year 4))
[:day-name opts expr] (&recur (temporal-case-expression expr opts :day-of-week 7)))) [:day-name opts expr] (&recur (temporal-case-expression expr opts :day-of-week 7))))

View File

@@ -2,8 +2,8 @@
"This namespace contains functionality that is not compatible with js." "This namespace contains functionality that is not compatible with js."
(:require (:require
[metabase.lib.schema.mbql-clause :as lib.schema.mbql-clause] [metabase.lib.schema.mbql-clause :as lib.schema.mbql-clause]
[metabase.lib.util.match :as lib.util.match] [metabase.util.malli :as mu]
[metabase.util.malli :as mu])) [metabase.util.match :as match]))
;;;; Following regex definitions are incompatible with Safari browser. Code is unused on FE. ;;;; Following regex definitions are incompatible with Safari browser. Code is unused on FE.
@@ -141,7 +141,7 @@
(mu/defn desugar-host-and-domain :- ::lib.schema.mbql-clause/clause (mu/defn desugar-host-and-domain :- ::lib.schema.mbql-clause/clause
"Unwrap host and domain." "Unwrap host and domain."
[expression :- ::lib.schema.mbql-clause/clause] [expression :- ::lib.schema.mbql-clause/clause]
(lib.util.match/replace-lite expression (match/replace expression
[:host opts expr] [:host opts expr]
;; TODO (Cam 8/18/25) -- seems weird that we don't support Regex literals in the regex clauses and have to call (str ...) ;; TODO (Cam 8/18/25) -- seems weird that we don't support Regex literals in the regex clauses and have to call (str ...)
(&recur [:regex-match-first opts expr (str host-regex)]) (&recur [:regex-match-first opts expr (str host-regex)])

View File

@@ -7,9 +7,9 @@
[metabase.lib.schema.mbql-clause :as lib.schema.mbql-clause] [metabase.lib.schema.mbql-clause :as lib.schema.mbql-clause]
[metabase.lib.schema.util :as lib.schema.util] [metabase.lib.schema.util :as lib.schema.util]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [some empty?]])) [metabase.util.performance :refer [some empty?]]))
(mr/def ::mbql-clause (mr/def ::mbql-clause
@@ -68,7 +68,7 @@
also fixes theoretically disallowed compound filters like `:and` with only a single subclause, and eliminates `nils` also fixes theoretically disallowed compound filters like `:and` with only a single subclause, and eliminates `nils`
and duplicate subclauses from the clauses." and duplicate subclauses from the clauses."
[x] [x]
(lib.util.match/replace-lite x (match/replace x
;; double negation, eliminate both ;; double negation, eliminate both
[:not opts [:not arg-opts arg-arg]] [:not opts [:not arg-opts arg-arg]]
(&recur (&recur

View File

@@ -27,9 +27,9 @@
[metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing] [metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing]
[metabase.lib.temporal-bucket :as lib.temporal-bucket] [metabase.lib.temporal-bucket :as lib.temporal-bucket]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.time :as u.time])) [metabase.util.time :as u.time]))
(defn- is-ref-for-column? [expr column] (defn- is-ref-for-column? [expr column]
@@ -39,7 +39,7 @@
(defn- contains-ref-for-column? [expr column] (defn- contains-ref-for-column? [expr column]
(letfn [(ref-for-column? [expr] (letfn [(ref-for-column? [expr]
(is-ref-for-column? expr column))] (is-ref-for-column? expr column))]
(lib.util.match/match-lite expr (match/match-one expr
(x :guard ref-for-column?) true))) (x :guard ref-for-column?) true)))
(mu/defn- remove-existing-filters-against-column* :- ::lib.schema/query (mu/defn- remove-existing-filters-against-column* :- ::lib.schema/query

View File

@@ -29,13 +29,13 @@
[metabase.lib.temporal-bucket :as lib.temporal-bucket] [metabase.lib.temporal-bucket :as lib.temporal-bucket]
[metabase.lib.types.isa :as lib.types.isa] [metabase.lib.types.isa :as lib.types.isa]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.util.unique-name-generator :as lib.util.unique-name-generator] [metabase.lib.util.unique-name-generator :as lib.util.unique-name-generator]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.i18n :as i18n] [metabase.util.i18n :as i18n]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [mapv run! some empty? not-empty get-in #?(:clj for)]])) [metabase.util.performance :refer [mapv run! some empty? not-empty get-in #?(:clj for)]]))
(defn- join? [x] (defn- join? [x]
@@ -72,7 +72,7 @@
columns from [[join-condition-lhs-columns]], and the RHS expression from [[join-condition-rhs-columns]]." columns from [[join-condition-lhs-columns]], and the RHS expression from [[join-condition-rhs-columns]]."
[condition :- [:maybe ::lib.schema.expression/boolean]] [condition :- [:maybe ::lib.schema.expression/boolean]]
(when condition (when condition
(lib.util.match/match-lite condition (match/match-one condition
[(_operator :guard lib.schema.join/condition-operators) [(_operator :guard lib.schema.join/condition-operators)
_opts _opts
(_lhs :guard lib.util/clause?) (_lhs :guard lib.util/clause?)
@@ -126,7 +126,7 @@
;; if we've specified `old-alias`, then update ANY `:field` clause using it to `new-alias` instead. ;; if we've specified `old-alias`, then update ANY `:field` clause using it to `new-alias` instead.
old-alias old-alias
(lib.util.match/replace-in join [:conditions] (match/replace-in join [:conditions]
(field :guard (and (lib.util/field-clause? field) (= (lib.join.util/current-join-alias field) old-alias))) (field :guard (and (lib.util/field-clause? field) (= (lib.join.util/current-join-alias field) old-alias)))
(with-join-alias field new-alias)) (with-join-alias field new-alias))
@@ -137,7 +137,7 @@
(mapv (fn [condition] (mapv (fn [condition]
(standard-join-condition-update-rhs condition (standard-join-condition-update-rhs condition
(fn [rhs] (fn [rhs]
(lib.util.match/replace-lite rhs (match/replace rhs
(field :guard lib.util/field-clause?) (field :guard lib.util/field-clause?)
(with-join-alias field new-alias))))) (with-join-alias field new-alias)))))
conditions))))) conditions)))))
@@ -484,7 +484,7 @@
conditions conditions
(mapv (fn [condition] (mapv (fn [condition]
(standard-join-condition-update-rhs condition (fn [rhs] (standard-join-condition-update-rhs condition (fn [rhs]
(lib.util.match/replace-lite rhs (match/replace rhs
(field :guard (and (lib.util/field-clause? field) (not (lib.join.util/current-join-alias field)))) (field :guard (and (lib.util/field-clause? field) (not (lib.join.util/current-join-alias field))))
(with-join-alias field join-alias))))) (with-join-alias field join-alias)))))
conditions))) conditions)))
@@ -567,7 +567,7 @@
join-alias)) join-alias))
(defn- add-alias-to-join-refs [query stage-number form join-alias join-cols] (defn- add-alias-to-join-refs [query stage-number form join-alias join-cols]
(lib.util.match/replace-lite form (match/replace form
(field :guard (and (lib.util/field-clause? field) (field :guard (and (lib.util/field-clause? field)
(lib.equality/find-matching-column query stage-number field join-cols))) (lib.equality/find-matching-column query stage-number field join-cols)))
(with-join-alias field join-alias))) (with-join-alias field join-alias)))
@@ -580,7 +580,7 @@
;; these cases, but only for conditions that look like the ones generated ;; these cases, but only for conditions that look like the ones generated
;; generated by the FE. These have the form home-field op join-field, ;; generated by the FE. These have the form home-field op join-field,
;; so we break ties by looking at the position of the field reference. ;; so we break ties by looking at the position of the field reference.
(lib.util.match/match-lite condition (match/match-one condition
[op op-opts (lhs :guard lib.util/field-clause?) (rhs :guard lib.util/field-clause?)] [op op-opts (lhs :guard lib.util/field-clause?) (rhs :guard lib.util/field-clause?)]
(let [lhs-alias (lib.join.util/current-join-alias lhs) (let [lhs-alias (lib.join.util/current-join-alias lhs)
rhs-alias (lib.join.util/current-join-alias rhs)] rhs-alias (lib.join.util/current-join-alias rhs)]

View File

@@ -30,11 +30,11 @@
[metabase.lib.schema.common :as lib.schema.common] [metabase.lib.schema.common :as lib.schema.common]
[metabase.lib.schema.metadata :as lib.schema.metadata] [metabase.lib.schema.metadata :as lib.schema.metadata]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [mapv select-keys some update-keys every? empty? not-empty get-in #?(:clj for)]])) [metabase.util.performance :refer [mapv select-keys some update-keys every? empty? not-empty get-in #?(:clj for)]]))
(mr/def ::col (mr/def ::col
@@ -170,7 +170,7 @@
(log/error e "Column metadata has invalid :lib/expression-name (this was probably incorrectly propagated from a previous stage) (QUE-1342)") (log/error e "Column metadata has invalid :lib/expression-name (this was probably incorrectly propagated from a previous stage) (QUE-1342)")
(log/debugf "In query:\n%s" (u/pprint-to-str query)) (log/debugf "In query:\n%s" (u/pprint-to-str query))
nil))] nil))]
(lib.util.match/match-lite expr (match/match-one expr
[:convert-timezone _opts _expr source-tz & _] [:convert-timezone _opts _expr source-tz & _]
source-tz))))] source-tz))))]
(cond-> col (cond-> col
@@ -240,7 +240,7 @@
[col :- ::kebab-cased-map [col :- ::kebab-cased-map
a-ref :- ::mbql.s/Reference] a-ref :- ::mbql.s/Reference]
(let [a-ref (remove-namespaced-options a-ref)] (let [a-ref (remove-namespaced-options a-ref)]
(lib.util.match/replace-lite a-ref (match/replace a-ref
[:field (id :guard pos-int?) opts] [:field (id :guard pos-int?) opts]
[:field id (not-empty (cond-> (dissoc opts :effective-type :inherited-temporal-unit) [:field id (not-empty (cond-> (dissoc opts :effective-type :inherited-temporal-unit)
(:source-field opts) (dissoc :join-alias) (:source-field opts) (dissoc :join-alias)

View File

@@ -18,12 +18,12 @@
[metabase.lib.schema.template-tag :as lib.schema.template-tag] [metabase.lib.schema.template-tag :as lib.schema.template-tag]
[metabase.lib.template-tags :as lib.template-tags] [metabase.lib.template-tags :as lib.template-tags]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk.util :as lib.walk.util] [metabase.lib.walk.util :as lib.walk.util]
[metabase.util.humanization :as u.humanization] [metabase.util.humanization :as u.humanization]
[metabase.util.i18n :as i18n] [metabase.util.i18n :as i18n]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [every? mapv select-keys some empty? not-empty]])) [metabase.util.performance :refer [every? mapv select-keys some empty? not-empty]]))
(defn- finish-tag [{tag-name :name :as tag}] (defn- finish-tag [{tag-name :name :as tag}]
@@ -49,7 +49,7 @@
(let [parsed (lib.parse/parse {} query-text)] (let [parsed (lib.parse/parse {} query-text)]
(loop [found {} (loop [found {}
[current & more] parsed] [current & more] parsed]
(let [[found more] (lib.util.match/match-lite current (let [[found more] (match/match-one current
(_ :guard string?) [found more] (_ :guard string?) [found more]
{:type ::lib.parse/param, :name tag-name} {:type ::lib.parse/param, :name tag-name}

View File

@@ -14,9 +14,9 @@
[metabase.lib.schema.order-by :as lib.schema.order-by] [metabase.lib.schema.order-by :as lib.schema.order-by]
[metabase.lib.schema.util :as lib.schema.util] [metabase.lib.schema.util :as lib.schema.util]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.util.i18n :as i18n] [metabase.util.i18n :as i18n]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.performance :refer [some mapv empty? not-empty #?(:clj for)]])) [metabase.util.performance :refer [some mapv empty? not-empty #?(:clj for)]]))
(lib.hierarchy/derive :asc ::order-by-clause) (lib.hierarchy/derive :asc ::order-by-clause)
@@ -189,7 +189,7 @@
([query :- ::lib.schema/query ([query :- ::lib.schema/query
current-order-by :- ::lib.schema.order-by/order-by] current-order-by :- ::lib.schema.order-by/order-by]
(let [lib-uuid (lib.options/uuid current-order-by)] (let [lib-uuid (lib.options/uuid current-order-by)]
(lib.util.match/replace-lite query (match/replace query
[direction {:lib/uuid (uuid :guard (= uuid lib-uuid))} _] [direction {:lib/uuid (uuid :guard (= uuid lib-uuid))} _]
(assoc &match 0 (opposite-direction direction)))))) (assoc &match 0 (opposite-direction direction))))))

View File

@@ -1,12 +1,12 @@
(ns metabase.lib.parameters.parse (ns metabase.lib.parameters.parse
(:refer-clojure :exclude [mapv]) (:refer-clojure :exclude [mapv])
(:require (:require
[clojure.core.match :refer [match]]
[clojure.string :as str] [clojure.string :as str]
[metabase.lib.parameters.parse.types :as lib.params.parse.types] [metabase.lib.parameters.parse.types :as lib.params.parse.types]
[metabase.lib.parse :as lib.parse] [metabase.lib.parse :as lib.parse]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [mapv]])) [metabase.util.performance :refer [mapv]]))
(mr/def ::parsed-token (mr/def ::parsed-token
@@ -76,23 +76,25 @@
normalize-card-tag)) normalize-card-tag))
(defn- ->param [value] (defn- ->param [value]
(match [value] (match/match-one value
[s :guard string?] (s :guard string?)
s s
[{:type :metabase.lib.parse/param {:type :metabase.lib.parse/param
:name param-name}] :name param-name}
(lib.params.parse.types/param {:k (or (match-and-normalize-tag-name param-name) (lib.params.parse.types/param {:k (or (match-and-normalize-tag-name param-name)
(str/trim param-name))}) (str/trim param-name))})
[{:type :metabase.lib.parse/function-param {:type :metabase.lib.parse/function-param
:name param-name :name param-name
:args args}] :args args}
(lib.params.parse.types/function-param {:function-name param-name, :args (mapv ->param args)}) (lib.params.parse.types/function-param {:function-name param-name, :args (mapv ->param args)})
[{:type :metabase.lib.parse/optional {:type :metabase.lib.parse/optional
:contents contents}] :contents contents}
(lib.params.parse.types/optional {:args (mapv ->param contents)}))) (lib.params.parse.types/optional {:args (mapv ->param contents)})
_ (throw (ex-info "Invalid value." {:value value}))))
(mu/defn parse :- [:sequential ::parsed-token] (mu/defn parse :- [:sequential ::parsed-token]
"Attempts to parse parameters in string `s`. Parses any optional clauses or parameters found, and returns a sequence "Attempts to parse parameters in string `s`. Parses any optional clauses or parameters found, and returns a sequence

View File

@@ -22,12 +22,12 @@
[metabase.lib.temporal-bucket :as lib.temporal-bucket] [metabase.lib.temporal-bucket :as lib.temporal-bucket]
[metabase.lib.types.isa :as lib.types.isa] [metabase.lib.types.isa :as lib.types.isa]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.i18n :as i18n] [metabase.util.i18n :as i18n]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [some select-keys mapv empty? #?(:clj for)]] [metabase.util.performance :refer [some select-keys mapv empty? #?(:clj for)]]
[weavejester.dependency :as dep])) [weavejester.dependency :as dep]))
@@ -126,7 +126,7 @@
"Add `:base-type` and `:effective-type` to options of fields in `x` using `metadata-provider`. Works on MBQL 5 fields. "Add `:base-type` and `:effective-type` to options of fields in `x` using `metadata-provider`. Works on MBQL 5 fields.
`:effective-type` is required for coerced fields to pass schema checks." `:effective-type` is required for coerced fields to pass schema checks."
[x metadata-provider :- ::lib.schema.metadata/metadata-provider] [x metadata-provider :- ::lib.schema.metadata/metadata-provider]
(if-let [field-ids (lib.util.match/match-many x (if-let [field-ids (match/match-many x
[:field [:field
(_opts :guard (and (map? _opts) (not (and (:base-type _opts) (:effective-type _opts))))) (_opts :guard (and (map? _opts) (not (and (:base-type _opts) (:effective-type _opts)))))
(id :guard (and (integer? id) (pos? id)))] (id :guard (and (integer? id) (pos? id)))]
@@ -134,7 +134,7 @@
id))] id))]
;; "pre-warm" the metadata provider ;; "pre-warm" the metadata provider
(do (lib.metadata/bulk-metadata metadata-provider :metadata/column field-ids) (do (lib.metadata/bulk-metadata metadata-provider :metadata/column field-ids)
(lib.util.match/replace-lite x (match/replace x
[:field [:field
(options :guard (and (map? options) (not (and (:base-type options) (options :guard (and (map? options) (not (and (:base-type options)
(:effective-type options))))) (:effective-type options)))))
@@ -228,7 +228,7 @@
(mapv (fn [[stage-number stage]] (mapv (fn [[stage-number stage]]
(-> stage (-> stage
(add-types-to-fields metadata-provider) (add-types-to-fields metadata-provider)
(lib.util.match/replace-lite (match/replace
[:expression [:expression
(opts :guard (and (map? opts) (not (and (:base-type opts) (opts :guard (and (map? opts) (not (and (:base-type opts)
(:effective-type opts))))) (:effective-type opts)))))

View File

@@ -17,12 +17,12 @@
[metabase.lib.schema.mbql-clause :as lib.schema.mbql-clause] [metabase.lib.schema.mbql-clause :as lib.schema.mbql-clause]
[metabase.lib.schema.util :as lib.schema.util] [metabase.lib.schema.util :as lib.schema.util]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.util.unique-name-generator :as lib.util.unique-name-generator] [metabase.lib.util.unique-name-generator :as lib.util.unique-name-generator]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :as perf :refer [every? mapv run! some empty? not-empty get-in #?(:clj for)]])) [metabase.util.performance :as perf :refer [every? mapv run! some empty? not-empty get-in #?(:clj for)]]))
(defn- stage-paths (defn- stage-paths
@@ -90,7 +90,7 @@
source-uuid->new-column (m/index-by :lib/source-uuid new-columns)] source-uuid->new-column (m/index-by :lib/source-uuid new-columns)]
(lib.util/update-query-stage (lib.util/update-query-stage
query-modified stage-number query-modified stage-number
#(lib.util.match/replace-lite #(match/replace
% %
[:field & _] [:field & _]
(let [old-matching-column (lib.equality/find-matching-column &match old-columns)] (let [old-matching-column (lib.equality/find-matching-column &match old-columns)]
@@ -140,7 +140,7 @@
remove-replace-fn location target-clause) remove-replace-fn location target-clause)
target-uuid (lib.options/uuid target-clause)] target-uuid (lib.options/uuid target-clause)]
(if (not= query result) (if (not= query result)
(lib.util.match/match-lite location (match/match-one location
[:expressions] [:expressions]
(-> result (-> result
(remove-local-references (remove-local-references
@@ -178,7 +178,7 @@
(when-let [clauses (get-in stage location)] (when-let [clauses (get-in stage location)]
(->> clauses (->> clauses
(keep (fn [clause] (keep (fn [clause]
(lib.util.match/match-lite clause (match/match-one clause
[(op :guard (= op target-op)) [(op :guard (= op target-op))
(opts :guard (or (empty? target-opts) (opts :guard (or (empty? target-opts)
(set/subset? (set target-opts) (set opts)))) (set/subset? (set target-opts) (set opts))))
@@ -332,7 +332,7 @@
(defn- local-replace-expression-references [stage target-ref-id replacement-ref] (defn- local-replace-expression-references [stage target-ref-id replacement-ref]
(let [replace-embedded-refs (fn replace-refs [stage] (let [replace-embedded-refs (fn replace-refs [stage]
(lib.util.match/replace-lite stage (match/replace stage
[:expression _ (id :guard (= id target-ref-id))] [:expression _ (id :guard (= id target-ref-id))]
(-> replacement-ref (-> replacement-ref
fresh-ref)))] fresh-ref)))]
@@ -375,7 +375,7 @@
(let [target-ref-id (:lib/desired-column-alias col) (let [target-ref-id (:lib/desired-column-alias col)
replaced-ref (lib.ref/ref (assoc replaced-col :lib/source :source/previous-stage))] replaced-ref (lib.ref/ref (assoc replaced-col :lib/source :source/previous-stage))]
(mapv (fn [target-ref] [target-ref (fresh-ref replaced-ref)]) (mapv (fn [target-ref] [target-ref (fresh-ref replaced-ref)])
(lib.util.match/match-many (lib.util/query-stage query next-stage-number) (match/match-many (lib.util/query-stage query next-stage-number)
[:field _ (id :guard (= id target-ref-id))] &match)))) [:field _ (id :guard (= id target-ref-id))] &match))))
(defn- typed-expression (defn- typed-expression
@@ -454,12 +454,12 @@
"Checks if two sets of join conditions are the same. We ignore the current join-aliases as those may be changing, "Checks if two sets of join conditions are the same. We ignore the current join-aliases as those may be changing,
and we ignore effective-type since `tweak-expression` above may have already added it, and in this case it will be irrelevant." and we ignore effective-type since `tweak-expression` above may have already added it, and in this case it will be irrelevant."
[new-join-alias new-join-conditions join-alias-b join-conditions-b] [new-join-alias new-join-conditions join-alias-b join-conditions-b]
(let [a-conds (lib.util.match/replace-lite new-join-conditions (let [a-conds (match/replace new-join-conditions
{:join-alias (ja :guard (= ja new-join-alias))} {:join-alias (ja :guard (= ja new-join-alias))}
(dissoc &match :join-alias :effective-type) (dissoc &match :join-alias :effective-type)
{:effective-type (_ :guard identity)} {:effective-type (_ :guard identity)}
(dissoc &match :effective-type)) (dissoc &match :effective-type))
b-conds (lib.util.match/replace-lite join-conditions-b b-conds (match/replace join-conditions-b
{:join-alias (ja :guard (= ja join-alias-b))} {:join-alias (ja :guard (= ja join-alias-b))}
(dissoc &match :join-alias :effective-type) (dissoc &match :join-alias :effective-type)
{:effective-type (_ :guard identity)} {:effective-type (_ :guard identity)}
@@ -555,7 +555,7 @@
(defn- replace-join-alias (defn- replace-join-alias
[a-join old-name new-name] [a-join old-name new-name]
(lib.util.match/replace-lite a-join (match/replace a-join
(field :guard (field-clause-with-join-alias? field old-name)) (field :guard (field-clause-with-join-alias? field old-name))
(lib.join/with-join-alias field new-name))) (lib.join/with-join-alias field new-name)))

View File

@@ -34,8 +34,8 @@
[metabase.lib.schema.settings :as lib.schema.settings] [metabase.lib.schema.settings :as lib.schema.settings]
[metabase.lib.schema.template-tag :as template-tag] [metabase.lib.schema.template-tag :as template-tag]
[metabase.lib.schema.util :as lib.schema.util] [metabase.lib.schema.util :as lib.schema.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [every? select-keys some empty? get-in]])) [metabase.util.performance :refer [every? select-keys some empty? get-in]]))
(comment metabase.lib.schema.expression.arithmetic/keep-me (comment metabase.lib.schema.expression.arithmetic/keep-me
@@ -420,7 +420,7 @@
(let [visible-join-alias? (some-fn visible-join-alias? (visible-join-alias?-fn stage))] (let [visible-join-alias? (some-fn visible-join-alias? (visible-join-alias?-fn stage))]
(or (or
(when (map? stage) (when (map? stage)
(lib.util.match/match-lite (dissoc stage :joins :lib/stage-metadata) (match/match-one (dissoc stage :joins :lib/stage-metadata)
[:field {:join-alias (join-alias :guard (and join-alias [:field {:join-alias (join-alias :guard (and join-alias
(not (visible-join-alias? join-alias))))} _id-or-name] (not (visible-join-alias? join-alias))))} _id-or-name]
(str "Invalid :field reference in stage " i ": no join named " (pr-str join-alias)))) (str "Invalid :field reference in stage " i ": no join named " (pr-str join-alias))))

View File

@@ -20,11 +20,11 @@
[metabase.lib.schema.metadata :as lib.schema.metadata] [metabase.lib.schema.metadata :as lib.schema.metadata]
[metabase.lib.stage.util] [metabase.lib.stage.util]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.util.unique-name-generator :as lib.util.unique-name-generator] [metabase.lib.util.unique-name-generator :as lib.util.unique-name-generator]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.i18n :as i18n] [metabase.util.i18n :as i18n]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.namespaces :as shared.ns] [metabase.util.namespaces :as shared.ns]
[metabase.util.performance :refer [mapv some not-empty get-in #?(:clj for)]])) [metabase.util.performance :refer [mapv some not-empty get-in #?(:clj for)]]))
@@ -167,7 +167,7 @@
;; Only include "late" expressions when required. ;; Only include "late" expressions when required.
;; "Late" expressions those like :offset which can't be used within the same query stage, like aggregations. ;; "Late" expressions those like :offset which can't be used within the same query stage, like aggregations.
:when (or include-late-exprs? :when (or include-late-exprs?
(not (lib.util.match/match-lite clause :offset clause)))] (not (match/match-one clause :offset clause)))]
(-> col (-> col
(assoc :lib/source :source/expressions, :lib/source-column-alias (:name col)) (assoc :lib/source :source/expressions, :lib/source-column-alias (:name col))
(u/assoc-default :effective-type (or (:base-type col) :type/*)))))) (u/assoc-default :effective-type (or (:base-type col) :type/*))))))

View File

@@ -17,12 +17,12 @@
[metabase.lib.schema.expression :as lib.schema.expression] [metabase.lib.schema.expression :as lib.schema.expression]
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.schema.ref :as lib.schema.ref] [metabase.lib.schema.ref :as lib.schema.ref]
[metabase.lib.util.match :as lib.util.match]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.i18n :as i18n] [metabase.util.i18n :as i18n]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [every? mapv select-keys update-keys some get-in #?(:clj for)]])) [metabase.util.performance :refer [every? mapv select-keys update-keys some get-in #?(:clj for)]]))
#?(:clj #?(:clj
@@ -502,7 +502,7 @@
query (fresh-uuids query (fn [old-uuid new-uuid] query (fresh-uuids query (fn [old-uuid new-uuid]
(vswap! remapping assoc! old-uuid new-uuid))) (vswap! remapping assoc! old-uuid new-uuid)))
remapping (persistent! @remapping)] remapping (persistent! @remapping)]
(lib.util.match/replace-lite query (match/replace query
[:aggregation opts old-uuid] [:aggregation opts old-uuid]
[:aggregation opts (or (remapping old-uuid) [:aggregation opts (or (remapping old-uuid)
(throw (ex-info "Could not convert old :aggregation ref to new UUIDs" (throw (ex-info "Could not convert old :aggregation ref to new UUIDs"

View File

@@ -1,9 +0,0 @@
(ns metabase.lib.util.match
(:require
[clojure.core.match]
[metabase.lib.util.match.impl])
(:require-macros [metabase.lib.util.match]))
(comment clojure.core.match/keep-me
metabase.lib.util.match/keep-me
metabase.lib.util.match.impl/keep-me)

View File

@@ -1,143 +0,0 @@
(ns metabase.lib.util.match.impl
"Internal implementation of the MBQL `match` and `replace` macros. Don't use these directly."
(:refer-clojure :exclude [mapv get-in empty?])
(:require
[metabase.util.performance :refer [mapv get-in empty?]]))
;; have to do this at runtime because we don't know if a symbol is a class or pred or whatever when we compile the macro
(defn match-with-pred-or-class
"Return a function to use for pattern matching via `core.match`'s `:guard` functionality based on the value of a
`pred-or-class` passed in as a pattern to `match` or `replace`.
(Class-based matching currently only works in Clojure. For ClojureScript, only predicate function matching works.)"
[pred-or-class]
(cond
;; If this function is ever used from JS, we need to figure out how to do this
#?@(:clj [(class? pred-or-class)
(partial instance? pred-or-class)])
(fn? pred-or-class)
pred-or-class
:else
;; this is dev-specific so we don't need to localize it
(throw (ex-info "Invalid pattern: don't know how to handle symbol." {:symbol pred-or-class}))))
(defn match-in-collection
"Internal impl for `match`. If `form` is a collection, call `match-fn` to recursively look for matches in it."
[match-fn clause-parents form]
{:pre [(fn? match-fn) (vector? clause-parents)]}
(cond
(map? form)
(reduce-kv (fn [acc k v]
(if-let [match (match-fn (conj clause-parents k) v)]
;; Deliberately not using into to avoid converting to transient and back.
(reduce conj acc match)
acc))
[] form)
(sequential? form)
(let [fst (first form)
k (if (keyword? fst)
(conj clause-parents fst)
clause-parents)]
(reduce (fn [acc v]
(if-let [match (match-fn k v)]
(reduce conj acc match)
acc))
[] form))))
(defn replace-in-collection
"Internal impl for `replace`. Recursively replace values in a collection using a `replace-fn`."
[replace-fn clause-parents form]
(cond
(map? form)
(reduce-kv (fn [form k v]
(assoc form k (replace-fn (conj clause-parents k) v)))
form form)
(sequential? form)
(mapv (partial replace-fn (if (keyword? (first form))
(conj clause-parents (first form))
clause-parents))
form)
:else form))
(defn update-in-unless-empty
"Like `update-in`, but only updates in the existing value is non-empty."
[m ks f]
(if (empty? (get-in m ks))
m
(update-in m ks f)))
(defn vector!
"Return nil if `obj` is not a vector, otherwise return `obj`."
[obj]
(when (vector? obj) obj))
(defn map!
"Return nil if `obj` is not a map, otherwise return `obj`."
[obj]
(when (map? obj) obj))
(defn count=
"Return true if collection `coll` has precisely `cnt` elements in it."
[coll cnt]
(= (count coll) cnt))
(defn count>=
"Return true if collection `coll` has `cnt` elements or more."
[coll cnt]
(>= (count coll) cnt))
(defn wrap-nil
"If `value` is nil, return `::wrapped-nil`, otherwise return `value`."
[value]
(if (nil? value) ::wrapped-nil value))
(defn unwrap-nil
"If `value` is `::wrapped-nil`, return nil, otherwise return `value`."
[value]
(when-not (= value ::wrapped-nil) value))
(defn- maybe-add-to-parents [clause-parents k]
(if (and clause-parents k)
(conj clause-parents k)
clause-parents))
(defn match-lite-in-collection
"Internal impl for `match-lite`. If `form` is a collection, call `match-fn` to recursively look for matches in it.
`clause-parents` is a sequence of keywords naming the parent top-level keys and clauses of the match."
[match-fn form clause-parents]
{:pre [(fn? match-fn)]}
(cond
(map? form)
(reduce-kv (fn [_ k v]
(when-some [match (match-fn v (maybe-add-to-parents clause-parents k))]
(reduced match)))
nil form)
(sequential? form)
(reduce (fn [_ v]
(let [fst (first form)]
(when-some [match (match-fn v (maybe-add-to-parents clause-parents (when (keyword? fst) fst)))]
(reduced match))))
nil form)))
(defn replace-lite-in-collection
"Internal impl for `replace-lite`. Recursively replace values in a collection using a `replace-fn`."
[replace-fn form clause-parents]
(cond
(map? form)
(reduce-kv (fn [form k v]
(let [repl (replace-fn v (maybe-add-to-parents clause-parents k))]
(cond-> form
(not (identical? v form)) (assoc k repl))))
form form)
(sequential? form)
(mapv #(let [fst (first form)]
(replace-fn % (maybe-add-to-parents clause-parents (when (keyword? fst) fst))))
form)
:else form))

View File

@@ -8,10 +8,10 @@
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.schema.mbql-clause :as lib.schema.mbql-clause] [metabase.lib.schema.mbql-clause :as lib.schema.mbql-clause]
[metabase.lib.schema.template-tag :as lib.schema.template-tag] [metabase.lib.schema.template-tag :as lib.schema.template-tag]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [not-empty]])) [metabase.util.performance :refer [not-empty]]))
(defn- transduce-stages (defn- transduce-stages
@@ -72,7 +72,7 @@
(lib.walk/walk-clauses (lib.walk/walk-clauses
query query
(fn [_query _path-type _stage-or-join-path clause] (fn [_query _path-type _stage-or-join-path clause]
(lib.util.match/match-lite clause (match/match-one clause
[:metric _opts (id :guard pos-int?)] [:metric _opts (id :guard pos-int?)]
(vswap! metric-ids conj! id) (vswap! metric-ids conj! id)
@@ -87,7 +87,7 @@
(lib.walk/walk-clauses (lib.walk/walk-clauses
query query
(fn [_query _path-type _stage-or-join-path clause] (fn [_query _path-type _stage-or-join-path clause]
(lib.util.match/match-lite clause (match/match-one clause
[:segment _opts (id :guard pos-int?)] [:segment _opts (id :guard pos-int?)]
(vswap! segment-ids conj! id) (vswap! segment-ids conj! id)
@@ -102,7 +102,7 @@
(lib.walk/walk-clauses (lib.walk/walk-clauses
query query
(fn [_query _path-type _stage-or-join-path clause] (fn [_query _path-type _stage-or-join-path clause]
(lib.util.match/match-lite clause (match/match-one clause
[:measure _opts (id :guard pos-int?)] [:measure _opts (id :guard pos-int?)]
(vswap! measure-ids conj! id) (vswap! measure-ids conj! id)
@@ -166,7 +166,7 @@
::lib.schema.mbql-clause/clause]] ::lib.schema.mbql-clause/clause]]
(let [field-ids (volatile! (transient #{})) (let [field-ids (volatile! (transient #{}))
walk-clause (fn [clause] walk-clause (fn [clause]
(lib.util.match/match-lite clause (match/match-one clause
[:field _opts (id :guard pos-int?)] [:field _opts (id :guard pos-int?)]
(vswap! field-ids conj! id) (vswap! field-ids conj! id)
@@ -184,7 +184,7 @@
(let [joined-field-ids (volatile! (transient #{})) (let [joined-field-ids (volatile! (transient #{}))
implicit-join-field-opt? #(and (:source-field %) (not (:join-alias %))) implicit-join-field-opt? #(and (:source-field %) (not (:join-alias %)))
walk-clause (fn [clause] walk-clause (fn [clause]
(lib.util.match/match-lite clause (match/match-one clause
[:field (opts :guard implicit-join-field-opt?) (id :guard pos-int?)] [:field (opts :guard implicit-join-field-opt?) (id :guard pos-int?)]
(vswap! joined-field-ids conj! id) (vswap! joined-field-ids conj! id)

View File

@@ -67,7 +67,6 @@
[metabase.lib.schema.common :as lib.schema.common] [metabase.lib.schema.common :as lib.schema.common]
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.schema.parameter :as lib.schema.parameter] [metabase.lib.schema.parameter :as lib.schema.parameter]
[metabase.lib.util.match :as lib.util.match]
[metabase.models.interface :as mi] [metabase.models.interface :as mi]
[metabase.models.serialization.resolve :as resolve] [metabase.models.serialization.resolve :as resolve]
[metabase.models.visualization-settings :as mb.viz] [metabase.models.visualization-settings :as mb.viz]
@@ -77,6 +76,7 @@
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[toucan2.core :as t2] [toucan2.core :as t2]
[toucan2.model :as t2.model] [toucan2.model :as t2.model]
[toucan2.realize :as t2.realize])) [toucan2.realize :as t2.realize]))
@@ -1205,7 +1205,7 @@
(mu/defn- collect-required-lib-uuids :- [:set ::lib.schema.common/uuid] (mu/defn- collect-required-lib-uuids :- [:set ::lib.schema.common/uuid]
[x] [x]
(set (set
(lib.util.match/match-many x (match/match-many x
[:aggregation (_opts :guard map?) (uuid :guard string?)] [:aggregation (_opts :guard map?) (uuid :guard string?)]
uuid))) uuid)))
@@ -1242,7 +1242,7 @@
(defn- export-mbql-ref (defn- export-mbql-ref
[mbql] [mbql]
(lib.util.match/replace-lite (normalize-mbql-ref mbql) (match/replace (normalize-mbql-ref mbql)
;; `pos-int?` guard is here to make the operation idempotent ;; `pos-int?` guard is here to make the operation idempotent
[:field (opts :guard map?) (id :guard pos-int?)] [:field (opts :guard map?) (id :guard pos-int?)]
[:field (export-mbql-map opts) (*export-field-fk* id)] [:field (export-mbql-map opts) (*export-field-fk* id)]
@@ -1321,7 +1321,7 @@
(defn- import-mbql-update-refs (defn- import-mbql-update-refs
[entity] [entity]
(lib.util.match/replace-lite entity (match/replace entity
[#{:field "field"} (opts :guard map?) (fully-qualified-name :guard vector?)] [#{:field "field"} (opts :guard map?) (fully-qualified-name :guard vector?)]
[:field (import-mbql-map opts) (*import-field-fk* fully-qualified-name)] [:field (import-mbql-map opts) (*import-field-fk* fully-qualified-name)]
@@ -1353,7 +1353,7 @@
[:measure (*import-fk* entity-id 'Measure)])) [:measure (*import-fk* entity-id 'Measure)]))
(defn- import-mbql-update-maps [x] (defn- import-mbql-update-maps [x]
(lib.util.match/replace-lite x (match/replace x
(m :guard map?) (m :guard map?)
(import-mbql-map m))) (import-mbql-map m)))
@@ -1383,7 +1383,7 @@
(declare ^:private mbql-deps-map) (declare ^:private mbql-deps-map)
(defn- mbql-deps-vector [entity] (defn- mbql-deps-vector [entity]
(lib.util.match/match-lite entity (match/match-one entity
[#{:field "field"} (opts :guard map?) (id :guard vector?)] [#{:field "field"} (opts :guard map?) (id :guard vector?)]
(into #{(field->path id)} (mbql-deps-map opts)) (into #{(field->path id)} (mbql-deps-map opts))
@@ -1698,7 +1698,7 @@
id))})))) id))}))))
(defn- import-visualizations [entity] (defn- import-visualizations [entity]
(lib.util.match/replace-lite entity (match/replace entity
[#{:field "field"} (opts :guard map?) (fully-qualified-name :guard vector?)] [#{:field "field"} (opts :guard map?) (fully-qualified-name :guard vector?)]
[:field (import-visualizations opts) (*import-field-fk* fully-qualified-name)] [:field (import-visualizations opts) (*import-field-fk* fully-qualified-name)]

View File

@@ -8,9 +8,9 @@
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.schema.parameter :as lib.schema.parameter] [metabase.lib.schema.parameter :as lib.schema.parameter]
[metabase.lib.util.match :as lib.util.match]
[metabase.models.visualization-settings :as mb.viz] [metabase.models.visualization-settings :as mb.viz]
[metabase.util.malli :as mu])) [metabase.util.malli :as mu]
[metabase.util.match :as match]))
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
@@ -70,12 +70,12 @@
(identity-hash? s)))) (identity-hash? s))))
;;; ============================================================ ;;; ============================================================
;;; import-mbql — depends only on protocols, lib.util.match, lib.schema.id ;;; import-mbql — depends only on protocols, match, lib.schema.id
;;; ============================================================ ;;; ============================================================
(defn- mbql-fully-qualified-names->ids* (defn- mbql-fully-qualified-names->ids*
[resolver entity] [resolver entity]
(lib.util.match/replace-lite entity (match/replace entity
[#{:field "field"} (opts :guard map?) (fully-qualified-name :guard vector?)] [#{:field "field"} (opts :guard map?) (fully-qualified-name :guard vector?)]
[:field (mbql-fully-qualified-names->ids* resolver opts) [:field (mbql-fully-qualified-names->ids* resolver opts)
(import-field-fk resolver fully-qualified-name)] (import-field-fk resolver fully-qualified-name)]
@@ -186,7 +186,7 @@
(defn- mbql-id->fully-qualified-name (defn- mbql-id->fully-qualified-name
[resolver mbql] [resolver mbql]
(lib.util.match/replace-lite (normalize mbql) (match/replace (normalize mbql)
;; `pos-int?` guard is here to make the operation idempotent ;; `pos-int?` guard is here to make the operation idempotent
[:field (opts :guard map?) (id :guard pos-int?)] [:field (opts :guard map?) (id :guard pos-int?)]
[:field (mbql-id->fully-qualified-name resolver opts) (export-field-fk resolver id)] [:field (mbql-id->fully-qualified-name resolver opts) (export-field-fk resolver id)]
@@ -224,7 +224,7 @@
inside it into portable references." inside it into portable references."
([entity] (export-mbql *export-resolver* entity)) ([entity] (export-mbql *export-resolver* entity))
([resolver entity] ([resolver entity]
(lib.util.match/replace-lite entity (match/replace entity
(_ :guard mbql-clause-tag) (_ :guard mbql-clause-tag)
(mbql-id->fully-qualified-name resolver &match) (mbql-id->fully-qualified-name resolver &match)

View File

@@ -15,7 +15,6 @@
[metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata :as lib.metadata]
[metabase.lib.metadata.protocols :as lib.metadata.protocols] [metabase.lib.metadata.protocols :as lib.metadata.protocols]
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.util.match :as lib.util.match]
[metabase.models.interface :as mi] [metabase.models.interface :as mi]
[metabase.permissions.core :as perms] [metabase.permissions.core :as perms]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
@@ -26,6 +25,7 @@
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms] [metabase.util.malli.schema :as ms]
[metabase.util.match :as match]
[toucan2.core :as t2])) [toucan2.core :as t2]))
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
@@ -126,7 +126,7 @@
(recur (lib/->legacy-MBQL query) parent-source-card-id in-sandbox?) (recur (lib/->legacy-MBQL query) parent-source-card-id in-sandbox?)
;; already legacy MBQL ;; already legacy MBQL
(apply merge-with merge-source-ids (apply merge-with merge-source-ids
(lib.util.match/match-many query (match/match-many query
(:and m {:qp/stage-is-from-source-card (id :guard identity)}) (:and m {:qp/stage-is-from-source-card (id :guard identity)})
(merge-with merge-source-ids (merge-with merge-source-ids
(when-not parent-source-card-id (when-not parent-source-card-id

View File

@@ -13,7 +13,6 @@
[metabase.lib.schema.info :as lib.schema.info] [metabase.lib.schema.info :as lib.schema.info]
[metabase.lib.schema.parameter :as lib.schema.parameter] [metabase.lib.schema.parameter :as lib.schema.parameter]
[metabase.lib.schema.template-tag :as lib.schema.template-tag] [metabase.lib.schema.template-tag :as lib.schema.template-tag]
[metabase.lib.util.match :as lib.util.match]
[metabase.parameters.schema :as parameters.schema] [metabase.parameters.schema :as parameters.schema]
[metabase.premium-features.core :refer [defenterprise]] [metabase.premium-features.core :refer [defenterprise]]
[metabase.queries.core :as queries] [metabase.queries.core :as queries]
@@ -33,6 +32,7 @@
[metabase.util.i18n :refer [tru]] [metabase.util.i18n :refer [tru]]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.performance :refer [mapv select-keys not-empty]] [metabase.util.performance :refer [mapv select-keys not-empty]]
^{:clj-kondo/ignore [:discouraged-namespace]} ^{:clj-kondo/ignore [:discouraged-namespace]}
[toucan2.core :as t2])) [toucan2.core :as t2]))
@@ -205,7 +205,7 @@
[{parameter-name :name, :keys [target]}] [{parameter-name :name, :keys [target]}]
(or (or
parameter-name parameter-name
(lib.util.match/match-lite target (match/match-one target
[:template-tag tag-name] [:template-tag tag-name]
(name tag-name)))) (name tag-name))))

View File

@@ -5,11 +5,11 @@
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata :as lib.metadata]
[metabase.lib.schema :as lib.schema] [metabase.lib.schema :as lib.schema]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
[metabase.util.i18n :refer [tru]] [metabase.util.i18n :refer [tru]]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.performance :refer [every? empty? not-empty]])) [metabase.util.performance :refer [every? empty? not-empty]]))
(mu/defn- should-add-implicit-fields? (mu/defn- should-add-implicit-fields?
@@ -61,7 +61,7 @@
(lib.walk/apply-f-for-stage-at-path updated-stage (assoc-in query path stage) path)))))) (lib.walk/apply-f-for-stage-at-path updated-stage (assoc-in query path stage) path))))))
(defn- has-window-function-aggregations? [stage] (defn- has-window-function-aggregations? [stage]
(or (lib.util.match/match-lite (mapcat stage [:aggregation :expressions]) (or (match/match-one (mapcat stage [:aggregation :expressions])
[#{:cum-sum :cum-count :offset} & _] [#{:cum-sum :cum-count :offset} & _]
true) true)
;; FIXME ;; FIXME

View File

@@ -16,13 +16,13 @@
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.schema.join :as lib.schema.join] [metabase.lib.schema.join :as lib.schema.join]
[metabase.lib.schema.metadata :as lib.schema.metadata] [metabase.lib.schema.metadata :as lib.schema.metadata]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
[metabase.util.i18n :refer [tru]] [metabase.util.i18n :refer [tru]]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [mapv some empty? not-empty get-in]])) [metabase.util.performance :refer [mapv some empty? not-empty get-in]]))
(defn- implicitly-joined-fields (defn- implicitly-joined-fields
@@ -34,7 +34,7 @@
[x] [x]
(into [] (into []
(distinct) (distinct)
(lib.util.match/match-many (dissoc x :lib/stage-metadata) (match/match-many (dissoc x :lib/stage-metadata)
[:field (opts :guard (and (:source-field opts) (not (:join-alias opts)))) _id-or-oname] [:field (opts :guard (and (:source-field opts) (not (:join-alias opts)))) _id-or-oname]
&match))) &match)))
@@ -119,7 +119,7 @@
(-> join (-> join
(assoc :alias new-alias) (assoc :alias new-alias)
(update :conditions lib.walk/walk-clauses* (fn [clause] (update :conditions lib.walk/walk-clauses* (fn [clause]
(lib.util.match/match-lite clause (match/match-one clause
[:field {:join-alias (ja :guard (= ja join-alias))} _id-or-name] [:field {:join-alias (ja :guard (= ja join-alias))} _id-or-name]
(lib/update-options &match assoc :join-alias new-alias)))))) (lib/update-options &match assoc :join-alias new-alias))))))
@@ -129,7 +129,7 @@
field-clauses-with-source-field :- [:sequential :mbql.clause/field]] field-clauses-with-source-field :- [:sequential :mbql.clause/field]]
(let [fk-field-infos (->> field-clauses-with-source-field (let [fk-field-infos (->> field-clauses-with-source-field
(keep (fn [clause] (keep (fn [clause]
(lib.util.match/match-lite clause (match/match-one clause
[:field (opts :guard (and (:source-field opts) (not (:join-alias opts)))) (id :guard integer?)] [:field (opts :guard (and (:source-field opts) (not (:join-alias opts)))) (id :guard integer?)]
(field-opts->fk-field-info metadata-providerable opts)))) (field-opts->fk-field-info metadata-providerable opts))))
distinct distinct
@@ -180,7 +180,7 @@
path :- ::lib.walk/path path :- ::lib.walk/path
stage :- ::lib.schema/stage] stage :- ::lib.schema/stage]
(or (when-let [fk-field-info->join-alias (not-empty (construct-fk-field-info->join-alias query path stage))] (or (when-let [fk-field-info->join-alias (not-empty (construct-fk-field-info->join-alias query path stage))]
(let [stage' (lib.util.match/replace-lite stage (let [stage' (match/replace stage
[:field (opts :guard (and (:source-field opts) (not (:join-alias opts)))) id-or-name] [:field (opts :guard (and (:source-field opts) (not (:join-alias opts)))) id-or-name]
(if-not (some #{:lib/stage-metadata} &parents) (if-not (some #{:lib/stage-metadata} &parents)
(let [join-alias (or (fk-field-info->join-alias (field-opts->fk-field-info query opts)) (let [join-alias (or (fk-field-info->join-alias (field-opts->fk-field-info query opts))
@@ -259,7 +259,7 @@
(let [next-stage (get-in query next-path)] (let [next-stage (get-in query next-path)]
(when-let [reused-join-aliases (not-empty (::reused-join-aliases next-stage))] (when-let [reused-join-aliases (not-empty (::reused-join-aliases next-stage))]
(when-let [referenced-fields (not-empty (when-let [referenced-fields (not-empty
(set (lib.util.match/match-many (dissoc next-stage :joins :lib/stage-metadata) (set (match/match-many (dissoc next-stage :joins :lib/stage-metadata)
[:field & _] [:field & _]
(when (contains? reused-join-aliases (lib/current-join-alias &match)) (when (contains? reused-join-aliases (lib/current-join-alias &match))
&match))))] &match))))]
@@ -306,7 +306,7 @@
"Get a set of join aliases that `join` has an immediate dependency on." "Get a set of join aliases that `join` has an immediate dependency on."
[join :- ::lib.schema.join/join] [join :- ::lib.schema.join/join]
(set (set
(lib.util.match/match-many (:conditions join) (match/match-many (:conditions join)
[:field {:join-alias (join-alias :guard (and join-alias (not= join-alias (:alias join))))} _id-or-name] [:field {:join-alias (join-alias :guard (and join-alias (not= join-alias (:alias join))))} _id-or-name]
join-alias))) join-alias)))
@@ -374,7 +374,7 @@
path :- ::lib.walk/path path :- ::lib.walk/path
stage :- ::lib.schema/stage] stage :- ::lib.schema/stage]
(when (and (= (:lib/type stage) :mbql.stage/mbql) (when (and (= (:lib/type stage) :mbql.stage/mbql)
(lib.util.match/match-lite stage (match/match-one stage
[:field (opts :guard (and (:source-field opts) (not (:join-alias opts)))) _id-or-name] true)) [:field (opts :guard (and (:source-field opts) (not (:join-alias opts)))) _id-or-name] true))
(when (and driver/*driver* (when (and driver/*driver*
(not (driver.u/supports? driver/*driver* :left-join (lib.metadata/database query)))) (not (driver.u/supports? driver/*driver* :left-join (lib.metadata/database query))))

View File

@@ -10,11 +10,11 @@
[metabase.lib.schema :as lib.schema] [metabase.lib.schema :as lib.schema]
[metabase.lib.schema.common :as lib.schema.common] [metabase.lib.schema.common :as lib.schema.common]
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [select-keys every? some not-empty get-in]])) [metabase.util.performance :refer [select-keys every? some not-empty get-in]]))
(mr/def ::column-type-info (mr/def ::column-type-info
@@ -115,7 +115,7 @@
:do-not-bucket-reason/not-all-values-are-auto-bucketable) :do-not-bucket-reason/not-all-values-are-auto-bucketable)
;; * do not autobucket clauses that are updating the time interval ;; * do not autobucket clauses that are updating the time interval
(lib.util.match/match-lite x (match/match-one x
[#{:+ :-} [#{:+ :-}
_ _
[#{:expression :field} _ _] [#{:expression :field} _ _]
@@ -164,7 +164,7 @@
{:base-type base-type {:base-type base-type
:effective-type (or effective-type base-type)}) :effective-type (or effective-type base-type)})
(wrap-clauses [x] (wrap-clauses [x]
(lib.util.match/replace-lite x (match/replace x
;; don't replace anything that's already bucketed or otherwise is not subject to autobucketing ;; don't replace anything that's already bucketed or otherwise is not subject to autobucketing
(x :guard (should-not-be-autobucketed? query stage-path x)) (x :guard (should-not-be-autobucketed? query stage-path x))
&match &match
@@ -188,7 +188,7 @@
{breakouts :breakout, :keys [filters], :as stage} :- ::lib.schema/stage] {breakouts :breakout, :keys [filters], :as stage} :- ::lib.schema/stage]
;; find any breakouts or filters in the query that are just plain `[:field-id ...]` clauses (unwrapped by any other ;; find any breakouts or filters in the query that are just plain `[:field-id ...]` clauses (unwrapped by any other
;; clause) ;; clause)
(if-let [unbucketed-clauses (lib.util.match/match-many (cons filters breakouts) (if-let [unbucketed-clauses (match/match-many (cons filters breakouts)
(clause :guard (should-not-be-autobucketed? query stage-path clause)) nil (clause :guard (should-not-be-autobucketed? query stage-path clause)) nil
[:expression & _] &match [:expression & _] &match
[:field & _] &match)] [:field & _] &match)]

View File

@@ -9,11 +9,11 @@
(:require (:require
[metabase.lib.schema :as lib.schema] [metabase.lib.schema :as lib.schema]
[metabase.lib.schema.common :as lib.schema.common] [metabase.lib.schema.common :as lib.schema.common]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
[metabase.util.i18n :refer [tru]] [metabase.util.i18n :refer [tru]]
[metabase.util.malli :as mu])) [metabase.util.malli :as mu]
[metabase.util.match :as match]))
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
@@ -40,7 +40,7 @@
;;; I guess we probably want this to work on join conditions as well as normal stage filters. ;;; I guess we probably want this to work on join conditions as well as normal stage filters.
(defn- auto-parse-filter-values-in-clause (defn- auto-parse-filter-values-in-clause
[_query _path-type _path clause] [_query _path-type _path clause]
(lib.util.match/match-lite clause (match/match-one clause
[:value [:value
(:and opts {:effective-type (et :guard (and et (not (isa? et :type/Text))))}) (:and opts {:effective-type (et :guard (and et (not (isa? et :type/Text))))})
(v :guard string?)] (v :guard string?)]

View File

@@ -9,13 +9,13 @@
[metabase.lib.schema.expression :as lib.schema.expression] [metabase.lib.schema.expression :as lib.schema.expression]
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.schema.metadata.fingerprint :as lib.schema.metadata.fingerprint] [metabase.lib.schema.metadata.fingerprint :as lib.schema.metadata.fingerprint]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.i18n :refer [tru]] [metabase.util.i18n :refer [tru]]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [get-in]])) [metabase.util.performance :refer [get-in]]))
(mr/def ::field-id-or-name->filters (mr/def ::field-id-or-name->filters
@@ -28,8 +28,8 @@
(reduce (reduce
(partial merge-with concat) (partial merge-with concat)
{} {}
(for [subclause (lib.util.match/match-many filters [#{:between :< :<= :> :>=} & _] &match) (for [subclause (match/match-many filters [#{:between :< :<= :> :>=} & _] &match)
field-id-or-name (lib.util.match/match-many subclause [:field _opts field-id-or-name] field-id-or-name)] field-id-or-name (match/match-many subclause [:field _opts field-id-or-name] field-id-or-name)]
{field-id-or-name [subclause]}))) {field-id-or-name [subclause]})))
(mu/defn- extract-bounds :- [:map [:min-value number?] [:max-value number?]] (mu/defn- extract-bounds :- [:map [:min-value number?] [:max-value number?]]
@@ -42,9 +42,9 @@
(let [{global-min :min, global-max :max} (get-in fingerprint [:type :type/Number]) (let [{global-min :min, global-max :max} (get-in fingerprint [:type :type/Number])
filter-clauses (get field-id-or-name->filters field-id-or-name) filter-clauses (get field-id-or-name->filters field-id-or-name)
;; [:between <field> <min> <max>] or [:< <field> <x>] ;; [:between <field> <min> <max>] or [:< <field> <x>]
user-maxes (lib.util.match/match-many filter-clauses user-maxes (match/match-many filter-clauses
[#{:< :<= :between} _opts & args] (last args)) [#{:< :<= :between} _opts & args] (last args))
user-mins (lib.util.match/match-many filter-clauses user-mins (match/match-many filter-clauses
[#{:> :>= :between} _opts _field min-val & _] min-val) [#{:> :>= :between} _opts _field min-val & _] min-val)
min-value (or (when (seq user-mins) min-value (or (when (seq user-mins)
(apply max user-mins)) (apply max user-mins))
@@ -99,7 +99,7 @@
query query
(fn [query path-type path clause] (fn [query path-type path clause]
(when (= path-type :lib.walk/stage) (when (= path-type :lib.walk/stage)
(lib.util.match/match-lite clause (match/match-one clause
;; first update all the `:binning` options ;; first update all the `:binning` options
[:field {:binning &truthy} _id-or-name] [:field {:binning &truthy} _id-or-name]
(update-binned-field query path (path->field-id-or-name->filters path) clause) (update-binned-field query path (path->field-id-or-name->filters path) clause)

View File

@@ -23,10 +23,10 @@
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata :as lib.metadata]
[metabase.lib.schema :as lib.schema] [metabase.lib.schema :as lib.schema]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.query-processor.schema :as qp.schema] [metabase.query-processor.schema :as qp.schema]
[metabase.util.malli :as mu])) [metabase.util.malli :as mu]
[metabase.util.match :as match]))
;;;; Pre-processing ;;;; Pre-processing
@@ -40,7 +40,7 @@
(map not= coll-1 coll-2))) (map not= coll-1 coll-2)))
(defn- update-clause [clause] (defn- update-clause [clause]
(lib.util.match/match-lite clause (match/match-one clause
;; cumulative count doesn't necessarily have a field-id arg ;; cumulative count doesn't necessarily have a field-id arg
[:cum-count opts] [:count opts] [:cum-count opts] [:count opts]
[:cum-count opts field] [:count opts field] [:cum-count opts field] [:count opts field]

View File

@@ -3,15 +3,15 @@
(:require (:require
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.schema :as lib.schema] [metabase.lib.schema :as lib.schema]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.performance :refer [select-keys]])) [metabase.util.performance :refer [select-keys]]))
(defn- desugar* (defn- desugar*
[stage-or-join] [stage-or-join]
(letfn [(desugar** [x] (letfn [(desugar** [x]
(lib.util.match/replace-lite x (match/replace x
(clause :guard lib/clause?) (clause :guard lib/clause?)
(lib/desugar-filter-clause clause)))] (lib/desugar-filter-clause clause)))]
(merge (merge

View File

@@ -10,7 +10,6 @@
[metabase.lib.schema :as lib.schema] [metabase.lib.schema :as lib.schema]
[metabase.lib.schema.expression :as lib.schema.expression] [metabase.lib.schema.expression :as lib.schema.expression]
[metabase.lib.schema.metadata :as lib.schema.metadata] [metabase.lib.schema.metadata :as lib.schema.metadata]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
[metabase.util :as u] [metabase.util :as u]
@@ -18,6 +17,7 @@
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [mapv not-empty get-in]])) [metabase.util.performance :refer [mapv not-empty get-in]]))
;;; "legacy macro" as used below means legacy Segment. ;;; "legacy macro" as used below means legacy Segment.
@@ -43,7 +43,7 @@
(lib.walk/walk-stages (lib.walk/walk-stages
query query
(fn [_query _path stage] (fn [_query _path stage]
(lib.util.match/match-many stage (match/match-many stage
[#{macro-type} _opts (id :guard pos-int?)] [#{macro-type} _opts (id :guard pos-int?)]
(conj! ids id)) (conj! ids id))
nil)) nil))
@@ -95,7 +95,7 @@
[_macro-type :- [:= :segment] [_macro-type :- [:= :segment]
stage :- ::lib.schema/stage stage :- ::lib.schema/stage
id->legacy-segment :- ::id->legacy-macro] id->legacy-segment :- ::id->legacy-macro]
(-> (lib.util.match/replace-lite stage (-> (match/replace stage
[:segment _opts (id :guard pos-int?)] [:segment _opts (id :guard pos-int?)]
(let [legacy-segment (get id->legacy-segment id) (let [legacy-segment (get id->legacy-segment id)
filter-clauses (legacy-macro-filters legacy-segment)] filter-clauses (legacy-macro-filters legacy-segment)]

View File

@@ -6,9 +6,9 @@
[metabase.lib.field.resolution :as lib.field.resolution] [metabase.lib.field.resolution :as lib.field.resolution]
[metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata :as lib.metadata]
[metabase.lib.schema :as lib.schema] [metabase.lib.schema :as lib.schema]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.performance :refer [get-in]])) [metabase.util.performance :refer [get-in]]))
(mu/defn- fix-bad-field-id-refs-in-stage :- [:maybe ::lib.schema/stage] (mu/defn- fix-bad-field-id-refs-in-stage :- [:maybe ::lib.schema/stage]
@@ -18,7 +18,7 @@
(let [first-stage-path (conj (pop (vec path)) 0) (let [first-stage-path (conj (pop (vec path)) 0)
source-table (:source-table (get-in query first-stage-path)) source-table (:source-table (get-in query first-stage-path))
update-fields (fn update-fields [form] update-fields (fn update-fields [form]
(lib.util.match/replace-lite form (match/replace form
;; don't recurse into joins. But should we update conditions tho. ;; don't recurse into joins. But should we update conditions tho.
{:lib/type :mbql/join} {:lib/type :mbql/join}
(update &match :conditions update-fields) (update &match :conditions update-fields)

View File

@@ -14,13 +14,13 @@
[metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata :as lib.metadata]
[metabase.lib.options :as lib.options] [metabase.lib.options :as lib.options]
[metabase.lib.schema :as lib.schema] [metabase.lib.schema :as lib.schema]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.i18n :refer [tru]] [metabase.util.i18n :refer [tru]]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.performance :refer [select-keys some]])) [metabase.util.performance :refer [select-keys some]]))
(defn- contains-metric-reference? (defn- contains-metric-reference?
@@ -78,7 +78,7 @@
"Replace :measure clauses in a stage with their actual aggregation expressions." "Replace :measure clauses in a stage with their actual aggregation expressions."
[stage :- ::lib.schema/stage [stage :- ::lib.schema/stage
id->measure :- [:map-of pos-int? :map]] id->measure :- [:map-of pos-int? :map]]
(lib.util.match/replace-lite stage (match/replace stage
[:measure opts (id :guard pos-int?)] [:measure opts (id :guard pos-int?)]
(b/cond (b/cond
:let [measure (get id->measure id)] :let [measure (get id->measure id)]

View File

@@ -7,10 +7,10 @@
[metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata :as lib.metadata]
[metabase.lib.options :as lib.options] [metabase.lib.options :as lib.options]
[metabase.lib.util :as lib.util] [metabase.lib.util :as lib.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.performance :as perf :refer [select-keys some empty? not-empty]])) [metabase.util.performance :as perf :refer [select-keys some empty? not-empty]]))
(defn- filters->condition (defn- filters->condition
@@ -137,12 +137,12 @@
(if-let [aggregations (lib/aggregations query stage-number)] (if-let [aggregations (lib/aggregations query stage-number)]
(let [columns (lib/visible-columns query stage-number)] (let [columns (lib/visible-columns query stage-number)]
(assoc-in query [:stages stage-number :aggregation] (assoc-in query [:stages stage-number :aggregation]
(lib.util.match/replace-lite aggregations (match/replace aggregations
[:metric _ metric-id] [:metric _ metric-id]
(if-let [{replacement :aggregation} (get lookup metric-id)] (if-let [{replacement :aggregation} (get lookup metric-id)]
;; We have to replace references from the source-metric with references appropriate for ;; We have to replace references from the source-metric with references appropriate for
;; this stage (expression/aggregation -> field, field-id to string) ;; this stage (expression/aggregation -> field, field-id to string)
(let [replacement (lib.util.match/replace-lite replacement (let [replacement (match/replace replacement
[#{:expression :field :aggregation} _ _] [#{:expression :field :aggregation} _ _]
(if-let [col (lib/find-matching-column &match columns)] (if-let [col (lib/find-matching-column &match columns)]
(lib/ref col) (lib/ref col)
@@ -160,7 +160,7 @@
(defn- find-metric-ids (defn- find-metric-ids
[x] [x]
(lib.util.match/match-many x (match/match-many x
[:metric _ (id :guard pos-int?)] [:metric _ (id :guard pos-int?)]
id)) id))
@@ -228,7 +228,7 @@
(defn- add-join-aliases (defn- add-join-aliases
[x source-field->join-alias] [x source-field->join-alias]
(lib.util.match/replace-lite x (match/replace x
[:field (opts :guard (and (source-field->join-alias (:source-field opts)) (not (:join-alias opts)))) _] [:field (opts :guard (and (source-field->join-alias (:source-field opts)) (not (:join-alias opts)))) _]
(assoc-in &match [1 :join-alias] (-> opts :source-field source-field->join-alias)))) (assoc-in &match [1 :join-alias] (-> opts :source-field source-field->join-alias))))

View File

@@ -9,13 +9,13 @@
[metabase.lib.schema.common :as lib.schema.common] [metabase.lib.schema.common :as lib.schema.common]
[metabase.lib.schema.expression :as lib.schema.expression] [metabase.lib.schema.expression :as lib.schema.expression]
[metabase.lib.schema.mbql-clause :as lib.schema.mbql-clause] [metabase.lib.schema.mbql-clause :as lib.schema.mbql-clause]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.date-2 :as u.date] [metabase.util.date-2 :as u.date]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [get-in]])) [metabase.util.performance :refer [get-in]]))
(def ^:private optimizable-units (def ^:private optimizable-units
@@ -29,7 +29,7 @@
(isa? expr-type :type/Temporal)))))) (isa? expr-type :type/Temporal))))))
(defn- optimizable-expr? [expr] (defn- optimizable-expr? [expr]
(lib.util.match/match-lite expr (match/match-one expr
[#{:field :expression} & _] [#{:field :expression} & _]
(and (temporal-ref? &match) (and (temporal-ref? &match)
(let [unit (or (lib/raw-temporal-bucket &match) :default)] (let [unit (or (lib/raw-temporal-bucket &match) :default)]
@@ -43,7 +43,7 @@
(defn- optimizable-temporal-value? (defn- optimizable-temporal-value?
"Can `temporal-value` clause can be optimized?" "Can `temporal-value` clause can be optimized?"
[temporal-value] [temporal-value]
(lib.util.match/match-lite temporal-value (match/match-one temporal-value
[:relative-datetime _opts #{0 :current}] [:relative-datetime _opts #{0 :current}]
true true
@@ -56,7 +56,7 @@
"Do datetime `field` clause and `temporal-value` clause have 'compatible' units that mean we'll be able to optimize "Do datetime `field` clause and `temporal-value` clause have 'compatible' units that mean we'll be able to optimize
the filter clause they're in?" the filter clause they're in?"
[field temporal-value] [field temporal-value]
(lib.util.match/match-lite temporal-value (match/match-one temporal-value
[:relative-datetime _opts #{0 :current}] [:relative-datetime _opts #{0 :current}]
true true
@@ -70,7 +70,7 @@
(defmethod can-optimize-filter? :default (defmethod can-optimize-filter? :default
[filter-clause] [filter-clause]
(lib.util.match/match-lite filter-clause (match/match-one filter-clause
[_tag [_tag
_opts _opts
(field :guard optimizable-expr?) (field :guard optimizable-expr?)
@@ -89,7 +89,7 @@
(defmethod can-optimize-filter? :>= (defmethod can-optimize-filter? :>=
[filter-clause] [filter-clause]
(lib.util.match/match-lite (match/match-one
filter-clause filter-clause
[_tag [_tag
_opts _opts
@@ -100,7 +100,7 @@
(defmethod can-optimize-filter? :< (defmethod can-optimize-filter? :<
[filter-clause] [filter-clause]
(lib.util.match/match-lite filter-clause (match/match-one filter-clause
[_tag [_tag
_opts _opts
;; Don't optimize < with column that has default temporal bucket ;; Don't optimize < with column that has default temporal bucket
@@ -110,7 +110,7 @@
(defmethod can-optimize-filter? :between (defmethod can-optimize-filter? :between
[filter-clause] [filter-clause]
(lib.util.match/match-lite filter-clause (match/match-one filter-clause
[:between [:between
_opts _opts
[(_offset :guard #{:+ :-}) [(_offset :guard #{:+ :-})
@@ -146,7 +146,7 @@
(:end (u.date/range t unit))) (:end (u.date/range t unit)))
(defn- change-temporal-unit-to-default [field] (defn- change-temporal-unit-to-default [field]
(lib.util.match/replace-lite field (match/replace field
[#{:field :expression} {:temporal-unit (_ :guard optimizable-units)} _id-or-name] [#{:field :expression} {:temporal-unit (_ :guard optimizable-units)} _id-or-name]
(lib/update-options &match assoc :temporal-unit :default) (lib/update-options &match assoc :temporal-unit :default)

View File

@@ -13,7 +13,6 @@
[metabase.lib.schema.parameter :as lib.schema.parameter] [metabase.lib.schema.parameter :as lib.schema.parameter]
[metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing] [metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing]
[metabase.lib.temporal-bucket :as lib.temporal-bucket] [metabase.lib.temporal-bucket :as lib.temporal-bucket]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
[metabase.query-processor.parameters.dates :as params.dates] [metabase.query-processor.parameters.dates :as params.dates]
@@ -21,6 +20,7 @@
[metabase.util.i18n :refer [tru]] [metabase.util.i18n :refer [tru]]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.performance :refer [every?]])) [metabase.util.performance :refer [every?]]))
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
@@ -77,7 +77,7 @@
[query :- ::lib.schema/query [query :- ::lib.schema/query
stage-path :- ::lib.walk/path stage-path :- ::lib.walk/path
{param-type :type, param-value :value, target :target, :as param} :- ::lib.schema.parameter/parameter] {param-type :type, param-value :value, target :target, :as param} :- ::lib.schema.parameter/parameter]
(let [a-ref (lib.util.match/match-lite target (let [a-ref (match/match-one target
[#{:field :expression} & _] [#{:field :expression} & _]
(lib/->mbql5 &match))] (lib/->mbql5 &match))]
(cond (cond
@@ -116,7 +116,7 @@
target-column :- [:or ::lib.schema.id/field :string] target-column :- [:or ::lib.schema.id/field :string]
temporal-unit :- ::lib.schema.temporal-bucketing/unit temporal-unit :- ::lib.schema.temporal-bucketing/unit
new-unit :- ::lib.schema.temporal-bucketing/unit] new-unit :- ::lib.schema.temporal-bucketing/unit]
(lib.util.match/replace-lite stage (match/replace stage
[#{:field :expression} [#{:field :expression}
(opts :guard (= (:temporal-unit opts) temporal-unit)) (opts :guard (= (:temporal-unit opts) temporal-unit))
(id-or-name :guard (= id-or-name target-column))] (id-or-name :guard (= id-or-name target-column))]
@@ -154,7 +154,7 @@
;; ignore `:template-tag` parameters... these may be lying around if we had a source card that was native and ;; ignore `:template-tag` parameters... these may be lying around if we had a source card that was native and
;; then replaced it with an MBQL source card. ;; then replaced it with an MBQL source card.
(lib.util.match/match-lite target [:template-tag & _] &match) (match/match-one target [:template-tag & _] &match)
(do (do
(log/warnf "Ignoring :template-tag parameter %s because this is an MBQL stage (path = %s)" (log/warnf "Ignoring :template-tag parameter %s because this is an MBQL stage (path = %s)"
(pr-str target) (pr-str target)

View File

@@ -1,8 +1,8 @@
(ns metabase.query-processor.middleware.persistence (ns metabase.query-processor.middleware.persistence
(:require (:require
[metabase.api.common :as api] [metabase.api.common :as api]
[metabase.lib.util.match :as lib.util.match] [metabase.permissions.core :as perms]
[metabase.permissions.core :as perms])) [metabase.util.match :as match]))
(defn substitute-persisted-query (defn substitute-persisted-query
"Removes persisted information if user is sandboxed or uses connection impersonation. `:persisted-info/native` is set "Removes persisted information if user is sandboxed or uses connection impersonation. `:persisted-info/native` is set
@@ -14,9 +14,9 @@
work, but for now we skip the cache in these cases." work, but for now we skip the cache in these cases."
[query] [query]
(if (and api/*current-user-id* (if (and api/*current-user-id*
(or (lib.util.match/match-lite query {:query-permissions/sandboxed-table &truthy} true) (or (match/match-one query {:query-permissions/sandboxed-table &truthy} true)
(perms/impersonation-enforced-for-db? (:database query)))) (perms/impersonation-enforced-for-db? (:database query))))
(lib.util.match/replace-lite query (match/replace query
{:persisted-info/native &truthy} {:persisted-info/native &truthy}
(dissoc &match :persisted-info/native)) (dissoc &match :persisted-info/native))
query)) query))

View File

@@ -3,12 +3,12 @@
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.schema :as lib.schema] [metabase.lib.schema :as lib.schema]
[metabase.lib.temporal-bucket :as lib.temporal-bucket] [metabase.lib.temporal-bucket :as lib.temporal-bucket]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.i18n :refer [tru]] [metabase.util.i18n :refer [tru]]
[metabase.util.malli :as mu])) [metabase.util.malli :as mu]
[metabase.util.match :as match]))
(mu/defn validate-temporal-bucketing (mu/defn validate-temporal-bucketing
"Make sure temporal bucketing of Fields (i.e., `:datetime-field` clauses) in this query is valid given the combination "Make sure temporal bucketing of Fields (i.e., `:datetime-field` clauses) in this query is valid given the combination
@@ -18,7 +18,7 @@
(lib.walk/walk-clauses (lib.walk/walk-clauses
query query
(fn [query _path-type path clause] (fn [query _path-type path clause]
(lib.util.match/match-lite clause (match/match-one clause
[:field (:and opts {:temporal-unit temporal-unit}) _id-or-name] [:field (:and opts {:temporal-unit temporal-unit}) _id-or-name]
(let [effective-type (lib.walk/apply-f-for-stage-at-path lib/type-of query path clause) (let [effective-type (lib.walk/apply-f-for-stage-at-path lib/type-of query path clause)
valid-units (lib.temporal-bucket/valid-units-for-type effective-type)] valid-units (lib.temporal-bucket/valid-units-for-type effective-type)]

View File

@@ -10,7 +10,6 @@
[metabase.lib.schema :as lib.schema] [metabase.lib.schema :as lib.schema]
[metabase.lib.schema.expression :as lib.schema.expression] [metabase.lib.schema.expression :as lib.schema.expression]
[metabase.lib.types.isa :as lib.types.isa] [metabase.lib.types.isa :as lib.types.isa]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
^{:clj-kondo/ignore [:deprecated-namespace]} [metabase.query-processor.store :as qp.store] ^{:clj-kondo/ignore [:deprecated-namespace]} [metabase.query-processor.store :as qp.store]
@@ -18,6 +17,7 @@
[metabase.util.date-2 :as u.date] [metabase.util.date-2 :as u.date]
[metabase.util.i18n :as i18n] [metabase.util.i18n :as i18n]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.performance :refer [select-keys]]) [metabase.util.performance :refer [select-keys]])
(:import (:import
(java.time LocalDate LocalDateTime LocalTime OffsetDateTime OffsetTime ZonedDateTime))) (java.time LocalDate LocalDateTime LocalTime OffsetDateTime OffsetTime ZonedDateTime)))
@@ -227,7 +227,7 @@
(defn- wrap-value-literals-in-clause (defn- wrap-value-literals-in-clause
[query path clause] [query path clause]
(lib.util.match/match-lite clause (match/match-one clause
;; two literals ;; two literals
[(tag :guard #{:= :!= :< :> :<= :>=}) opts (x :guard raw-value?) (y :guard raw-value?)] [(tag :guard #{:= :!= :< :> :<= :>=}) opts (x :guard raw-value?) (y :guard raw-value?)]
(let [x-type (lib.schema.expression/type-of-resolved x) (let [x-type (lib.schema.expression/type-of-resolved x)
@@ -282,7 +282,7 @@
(defn unwrap-value-literal (defn unwrap-value-literal
"Extract value literal from `:value` form or returns form as is if not a `:value` form." "Extract value literal from `:value` form or returns form as is if not a `:value` form."
[maybe-value-form] [maybe-value-form]
(lib.util.match/match-lite maybe-value-form (match/match-one maybe-value-form
[:value x & _] x [:value x & _] x
_ maybe-value-form)) _ maybe-value-form))

View File

@@ -15,10 +15,10 @@
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.schema.expression :as lib.schema.expression] [metabase.lib.schema.expression :as lib.schema.expression]
[metabase.lib.schema.parameter :as lib.schema.parameter] [metabase.lib.schema.parameter :as lib.schema.parameter]
[metabase.lib.util.match :as lib.util.match]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
[metabase.util.i18n :refer [tru]] [metabase.util.i18n :refer [tru]]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.performance :refer [get-in]])) [metabase.util.performance :refer [get-in]]))
(mu/defn- operator-arity :- [:maybe [:enum :unary :binary :variadic]] (mu/defn- operator-arity :- [:maybe [:enum :unary :binary :variadic]]
@@ -80,7 +80,7 @@
`:type qp.error-type/invalid-parameter` if arity is incorrect." `:type qp.error-type/invalid-parameter` if arity is incorrect."
[param] [param]
(let [{param-type :type, [a b :as param-value] :value, target :target, options :options} (normalize-param param) (let [{param-type :type, [a b :as param-value] :value, target :target, options :options} (normalize-param param)
field-ref (or (lib.util.match/match-lite target field-ref (or (match/match-one target
[#{:field :expression} & _] [#{:field :expression} & _]
(lib/->mbql5 &match)) (lib/->mbql5 &match))
(throw (ex-info (format "Invalid target: expected :field ref, got: %s" (pr-str target)) (throw (ex-info (format "Invalid target: expected :field ref, got: %s" (pr-str target))

View File

@@ -52,7 +52,6 @@
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.schema.join :as lib.schema.join] [metabase.lib.schema.join :as lib.schema.join]
[metabase.lib.schema.metadata :as lib.schema.metadata] [metabase.lib.schema.metadata :as lib.schema.metadata]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
[metabase.query-processor.middleware.annotate.legacy-helper-fns :as annotate.legacy-helper-fns] [metabase.query-processor.middleware.annotate.legacy-helper-fns :as annotate.legacy-helper-fns]
@@ -62,6 +61,7 @@
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.performance :refer [mapv select-keys some empty? not-empty get-in]])) [metabase.util.performance :refer [mapv select-keys some empty? not-empty get-in]]))
(mu/defn- ^:dynamic *escape-alias-fn* :- :string (mu/defn- ^:dynamic *escape-alias-fn* :- :string
@@ -217,7 +217,7 @@
[query :- ::lib.schema/query [query :- ::lib.schema/query
path :- ::lib.walk/path path :- ::lib.walk/path
stage :- ::lib.schema/stage.mbql] stage :- ::lib.schema/stage.mbql]
(lib.util.match/replace-lite stage (match/replace stage
;; don't recurse into the metadata or joins -- [[lib.walk]] will take care of that recursion for us. ;; don't recurse into the metadata or joins -- [[lib.walk]] will take care of that recursion for us.
(_ :guard (some #{:lib/stage-metadata :joins} &parents)) (_ :guard (some #{:lib/stage-metadata :joins} &parents))
&match &match
@@ -268,7 +268,7 @@
by-source-alias :- [:map-of :string [:sequential ::lib.schema.metadata/column]] by-source-alias :- [:map-of :string [:sequential ::lib.schema.metadata/column]]
by-expression-name :- [:map-of :string ::lib.schema.metadata/column] by-expression-name :- [:map-of :string ::lib.schema.metadata/column]
stage :- ::lib.schema/stage.mbql] stage :- ::lib.schema/stage.mbql]
(lib.util.match/replace-lite stage (match/replace stage
;; don't recurse into the metadata or joins -- [[lib.walk]] will take care of that recursion for us. ;; don't recurse into the metadata or joins -- [[lib.walk]] will take care of that recursion for us.
(_ :guard (some #{:lib/stage-metadata :joins ::resolved} &parents)) (_ :guard (some #{:lib/stage-metadata :joins ::resolved} &parents))
&match &match
@@ -410,7 +410,7 @@
(add-source-to-field-ref query parent-stage-path field-ref col))) (add-source-to-field-ref query parent-stage-path field-ref col)))
update-conditions (fn [conditions] update-conditions (fn [conditions]
;; the only kind of ref join conditions can have is a `:field` ref ;; the only kind of ref join conditions can have is a `:field` ref
(lib.util.match/replace-lite conditions (match/replace conditions
[:field opts _id-or-name] [:field opts _id-or-name]
(if (= (:join-alias opts) (:alias join)) (if (= (:join-alias opts) (:alias join))
;; a field ref that comes from THIS join needs to get the desired alias returned ;; a field ref that comes from THIS join needs to get the desired alias returned

View File

@@ -10,10 +10,10 @@
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.equality :as lib.equality] [metabase.lib.equality :as lib.equality]
[metabase.lib.schema :as lib.schema] [metabase.lib.schema :as lib.schema]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.performance :refer [select-keys some]])) [metabase.util.performance :refer [select-keys some]]))
;; Mark all Fields at the new top level as `:qp/ignore-coercion` so QP implementations know not to apply coercion ;; Mark all Fields at the new top level as `:qp/ignore-coercion` so QP implementations know not to apply coercion
@@ -22,7 +22,7 @@
;; fail, but it might still make sense. For example, #48721 would have been avoided by unconditional marking. ;; fail, but it might still make sense. For example, #48721 would have been avoided by unconditional marking.
(defn- contains-expression-refs? [location] (defn- contains-expression-refs? [location]
(lib.util.match/match-lite location [:expression & _] true)) (match/match-one location [:expression & _] true))
(defn- should-nest-expressions? [query path] (defn- should-nest-expressions? [query path]
(and (lib.walk/apply-f-for-stage-at-path lib/mbql-stage? query path) (and (lib.walk/apply-f-for-stage-at-path lib/mbql-stage? query path)

View File

@@ -10,13 +10,13 @@
[metabase.lib.schema :as lib.schema] [metabase.lib.schema :as lib.schema]
[metabase.lib.schema.metadata :as lib.schema.metadata] [metabase.lib.schema.metadata :as lib.schema.metadata]
[metabase.lib.schema.util :as lib.schema.util] [metabase.lib.schema.util :as lib.schema.util]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.match :as match]
[metabase.util.performance :refer [mapv select-keys some not-empty]])) [metabase.util.performance :refer [mapv select-keys some not-empty]]))
(defn- stage-has-window-aggregation? [stage] (defn- stage-has-window-aggregation? [stage]
(lib.util.match/match-lite (:aggregation stage) (match/match-one (:aggregation stage)
[#{:cum-sum :cum-count :offset} & _] true)) [#{:cum-sum :cum-count :offset} & _] true))
(defn- stage-has-breakout? [stage] (defn- stage-has-breakout? [stage]
@@ -32,7 +32,7 @@
[tag [tag
(select-keys opts [:join-alias :temporal-unit :bucketing]) (select-keys opts [:join-alias :temporal-unit :bucketing])
id-or-name])) id-or-name]))
(lib.util.match/match-many (concat (:breakout stage) (:aggregation stage) (:expressions stage)) (match/match-many (concat (:breakout stage) (:aggregation stage) (:expressions stage))
[#{:field :expression} & _] &match))) [#{:field :expression} & _] &match)))
(mu/defn- new-first-stage :- ::lib.schema/stage (mu/defn- new-first-stage :- ::lib.schema/stage
@@ -57,7 +57,7 @@
(mu/defn- update-second-stage-refs :- ::lib.schema/stage (mu/defn- update-second-stage-refs :- ::lib.schema/stage
[stage :- ::lib.schema/stage [stage :- ::lib.schema/stage
first-stage-cols :- [:sequential ::lib.schema.metadata/column]] first-stage-cols :- [:sequential ::lib.schema.metadata/column]]
(lib.util.match/replace-lite stage (match/replace stage
[#{:field :expression} & _] [#{:field :expression} & _]
(if-let [col (when-not (some #{:expressions} &parents) (if-let [col (when-not (some #{:expressions} &parents)
(lib.equality/find-matching-column &match first-stage-cols))] (lib.equality/find-matching-column &match first-stage-cols))]

View File

@@ -1,12 +1,12 @@
(ns metabase.revisions.models.revision.diff (ns metabase.revisions.models.revision.diff
(:require (:require
[clojure.core.match :refer [match]]
[clojure.data :as data] [clojure.data :as data]
[metabase.util.i18n :refer [deferred-tru]] [metabase.util.i18n :refer [deferred-tru]]
[metabase.util.match :as match]
[toucan2.core :as t2])) [toucan2.core :as t2]))
(defn- match-1 [k v1 v2 identifier] (defn- match-1 [k v1 v2 identifier]
(match [k v1 v2] (match/match-one [k v1 v2]
[:name _ _] [:name _ _]
(deferred-tru "renamed {0} from \"{1}\" to \"{2}\"" identifier v1 v2) (deferred-tru "renamed {0} from \"{1}\" to \"{2}\"" identifier v1 v2)
@@ -44,12 +44,10 @@
(deferred-tru "changed the filters") (deferred-tru "changed the filters")
[:embedding_params _ _] [:embedding_params _ _]
(deferred-tru "changed the embedding parameters") (deferred-tru "changed the embedding parameters")))
:else nil))
(defn- match-2 [k v1 v2 identifier] (defn- match-2 [k v1 v2 identifier]
(match [k v1 v2] (match/match-one [k v1 v2]
[:archived _ after] [:archived _ after]
(if after (if after
(deferred-tru "trashed {0}" identifier) (deferred-tru "trashed {0}" identifier)
@@ -122,7 +120,8 @@
[#{:table_id :database_id :query_type} _ _] [#{:table_id :database_id :query_type} _ _]
nil nil
:else nil)) ;; Don't recurse, bail on fallthrough.
_ nil))
(defn- diff-string [k v1 v2 identifier] (defn- diff-string [k v1 v2 identifier]
(or (match-1 k v1 v2 identifier) (or (match-1 k v1 v2 identifier)

View File

@@ -1,215 +1,10 @@
(ns metabase.lib.util.match (ns metabase.util.match
"Internal implementation of the MBQL `match` and `replace` macros. Don't use these directly." "Leaner reimplementation of `clojure.core.match` macros with mostly compatible syntax and much smaller bytecode
footprint."
(:refer-clojure :exclude [every? run! some mapv replace empty?]) (:refer-clojure :exclude [every? run! some mapv replace empty?])
(:require (:require
[clojure.core.match] [metabase.util.match.impl]
[metabase.lib.util.match.impl] [metabase.util.performance :as perf :refer [empty? every? mapv run! some]]))
[metabase.util.performance :as perf :refer [every? run! some mapv empty?]]
[net.cgrand.macrovich :as macros]))
(defn- generate-pattern
"Generate a single appropriate pattern for use with core.match based on the `pattern` input passed into `match` or
`replace`."
[pattern]
(cond
(keyword? pattern)
[[pattern '& '_]]
(and (set? pattern) (every? keyword? pattern))
[[`(:or ~@pattern) '& '_]]
;; special case for `_`, we'll let you match anything with that
(= pattern '_)
[pattern]
(symbol? pattern)
`[(~'_ :guard (metabase.lib.util.match.impl/match-with-pred-or-class ~pattern))]
:else
[pattern]))
(defn- recur-form? [form]
(and (seq? form)
(= 'recur (first form))))
(defn- rewrite-recurs
"Replace any `recur` forms with ones that include the implicit `&parents` arg."
[fn-name result-form]
(perf/postwalk
(fn [form]
(if (recur-form? form)
;; we *could* use plain `recur` here, but `core.match` cannot apply code size optimizations if a `recur` form
;; is present. Instead, just do a non-tail-call-optimized call to the pattern fn so `core.match` can generate
;; efficient code.
;;
;; (recur [:new-clause ...]) ; -> (match-123456 &parents [:new-clause ...])
`(~fn-name ~'&parents ~@(rest form))
form))
result-form))
(defn- generate-patterns-and-results
"Generate the `core.match` patterns and results given the input to our macros.
`wrap-result-forms?` will wrap the results parts of the pairs in a vector, so we do something like `(reduce concat)`
on all of the results to return a sequence of matches for `match`."
[fn-name patterns-and-results & {:keys [wrap-result-forms?]}]
(mapcat (fn [[pattern result]]
[(generate-pattern pattern) (let [result (rewrite-recurs fn-name result)]
(if (or (not wrap-result-forms?)
(and (seq? result)
(= fn-name (first result))))
result
[result]))])
(partition 2 2 ['&match] patterns-and-results)))
(defn- skip-else-clause?
"If the last pattern passed in was `_`, we can skip generating the default `:else` clause, because it will never
match."
;; TODO - why don't we just let people pass their own `:else` clause instead?
[patterns-and-results]
(= '_ (second (reverse patterns-and-results))))
(defmethod clojure.core.match/emit-pattern-for-syntax [:isa? :default]
[[_ parent]] {:clojure.core.match/tag ::isa? :parent parent})
(defmethod clojure.core.match/to-source ::isa?
[{parent :parent} ocr]
`(isa? ~ocr ~parent))
(defmacro match**
"Internal impl for `match` and `replace` macros."
[& args]
(macros/case
:clj `(clojure.core.match/match ~@args)
:cljs `(cljs.core.match/match ~@args)))
(defmacro match*
"Internal impl for `match`. Generate a pattern-matching function using `core.match`, and call it with `form`."
[form patterns-and-results]
(let [match-fn-symb (gensym "match-")]
`(seq
(filter
some?
((fn ~match-fn-symb [~'&parents ~'&match]
(match** [~'&match]
~@(generate-patterns-and-results match-fn-symb patterns-and-results, :wrap-result-forms? true)
~@(when-not (skip-else-clause? patterns-and-results)
[:else `(metabase.lib.util.match.impl/match-in-collection ~match-fn-symb ~'&parents ~'&match)])))
[]
~form)))))
(defmacro match
"Return a sequence of things that match a `pattern` or `patterns` inside `x`, presumably a query, returning `nil` if
there are no matches. Recurses through maps and sequences. `pattern` can be one of several things:
* Keyword name of an MBQL clause
* Set of keyword names of MBQL clauses. Matches any clauses with those names
* A `core.match` pattern
* A symbol naming a class.
* A symbol naming a predicate function
* `_`, which will match anything
Examples:
;; keyword pattern
(match {:fields [[:field 10 nil]]} :field) ; -> [[:field 10 nil]]
;; set of keywords
(match some-query #{:field :expression}) ; -> [[:field 10 nil], [:expression \"wow\"], ...]
;; `core.match` patterns:
;; match any `:field` clause with two args (which should be all of them)
(match some-query [:field _ _])
;; match any `:field` clause with integer ID > 100
(match some-query [:field (_ :guard (every-pred integer? #(> % 100)))]) ; -> [[:field 200 nil], ...]
;; symbol naming a Class
;; match anything that is an instance of that class
(match some-query java.util.Date) ; -> [[#inst \"2018-10-08\", ...]
;; symbol naming a predicate function
;; match anything that satisfies that predicate
(match some-query (every-pred integer? even?)) ; -> [2 4 6 8]
;; match anything with `_`
(match 100 `_`) ; -> 100
### Using `core.match` patterns
See [`core.match` documentation](`https://github.com/clojure/core.match/wiki`) for more details.
Pattern-matching works almost exactly the way it does when using `core.match**` directly, with a few
differences:
* `mbql.util/match` returns a sequence of everything that matches, rather than the first match it finds
* patterns are automatically wrapped in vectors for you when appropriate
* things like keywords and classes are automatically converted to appropriate patterns for you
* this macro automatically recurses through sequences and maps as a final `:else` clause. If you don't want to
automatically recurse, use a catch-all pattern (such as `_`). Our macro implementation will optimize out this
`:else` clause if the last pattern is `_`
### Returning something other than the exact match with result body
By default, `match` returns whatever matches the pattern you pass in. But what if you only want to return part of
the match? You can, using `core.match` binding facilities. Bind relevant things in your pattern and pass in the
optional result body. Whatever result body returns will be returned by `match`:
;; just return the IDs of Field ID clauses
(match some-query [:field (id :guard integer?) _] id) ; -> [1 2 3]
You can also use result body to filter results; any `nil` values will be skipped:
(match some-query [:field (id :guard integer?) _]
(when (even? id)
id))
;; -> [2 4 6 8]
Of course, it's more efficient to let `core.match` compile an efficient matching function, so prefer using
patterns with `:guard` where possible.
You can also call `recur` inside result bodies, to use the same matching logic against a different value.
### `&match` and `&parents` anaphors
For more advanced matches, like finding a `:field` clauses nested anywhere inside another clause, `match` binds a
pair of anaphors inside the result body for your convenience. `&match` is bound to the entire match, regardless of
how you may have destructured it; `&parents` is bound to a sequence of keywords naming the parent top-level keys and
clauses of the match.
(lib.util.match/match {:filter [:time-interval [:field 1 nil] :current :month]} :field
;; &parents will be [:filter :time-interval]
(when (contains? (set &parents) :time-interval)
&match))
;; -> [[:field 1 nil]]"
{:style/indent :defn}
[x & patterns-and-results]
;; Actual implementation of these macros is in `mbql.util.match`. They're in a separate namespace because they have
;; lots of other functions and macros they use for their implementation (which means they have to be public) that we
;; would like to discourage you from using directly.
`(match* ~x ~patterns-and-results))
(defmacro match-one
"Like `match` but returns a single match rather than a sequence of matches."
{:style/indent :defn}
[x & patterns-and-results]
`(first (match* ~x ~patterns-and-results)))
;; TODO - it would be ultra handy to have a `match-all` function that could handle clauses with recursive matches,
;; e.g. with a query like
;;
;; {:query {:source-table 1, :joins [{:source-table 2, ...}]}}
;;
;; it would be useful to be able to do
;;
;;
;; ;; get *all* the source tables
;; (lib.util.match/match-all query
;; (&match :guard (every-pred map? :source-table))
;; (:source-table &match))
(defn- parse-pattern (defn- parse-pattern
"Parse a pattern vector into bindings and conditions" "Parse a pattern vector into bindings and conditions"
@@ -272,20 +67,20 @@
(vswap! bindings conj [(:symbol parsed) value])) (vswap! bindings conj [(:symbol parsed) value]))
:vector (let [s (if (symbol? value) value (gensym "vec")) :vector (let [s (if (symbol? value) value (gensym "vec"))
cnt (count parts)] cnt (count parts)]
(vswap! bindings conj (with-meta [s `(metabase.lib.util.match.impl/vector! ~value)] (vswap! bindings conj (with-meta [s `(metabase.util.match.impl/vector! ~value)]
{:vector-check true})) {:vector-check true}))
(dorun (map-indexed #(process-pattern %2 (with-meta (list `nth s %1 nil) (or (meta s) (dorun (map-indexed #(process-pattern %2 (with-meta (list `nth s %1 nil) (or (meta s)
{:depends-on s})) {:depends-on s}))
bindings conditions return) parts)) bindings conditions return) parts))
(when (pos? cnt) (when (pos? cnt)
(vswap! conditions conj (with-meta (list (if rest-part (vswap! conditions conj (with-meta (list (if rest-part
`metabase.lib.util.match.impl/count>= `metabase.util.match.impl/count>=
`metabase.lib.util.match.impl/count=) s cnt) `metabase.util.match.impl/count=) s cnt)
{:depends-on s}))) {:depends-on s})))
(when rest-part (when rest-part
(process-pattern rest-part `(into [] (drop ~cnt) ~s) bindings conditions false))) (process-pattern rest-part `(into [] (drop ~cnt) ~s) bindings conditions false)))
:map (let [s (if (symbol? value) value (gensym "map"))] :map (let [s (if (symbol? value) value (gensym "map"))]
(vswap! bindings conj [s `(metabase.lib.util.match.impl/map! ~value)]) (vswap! bindings conj [s `(metabase.util.match.impl/map! ~value)])
(run! (fn [[k v]] (run! (fn [[k v]]
(condp = v (condp = v
'&truthy (vswap! conditions conj (with-meta (list `get s k) {:depends-on s})) '&truthy (vswap! conditions conj (with-meta (list `get s k) {:depends-on s}))
@@ -311,13 +106,13 @@
(when predicate (when predicate
;; Make sure that the predicate is an invocation snippet, not a lambda as in regular `match` syntax. ;; Make sure that the predicate is an invocation snippet, not a lambda as in regular `match` syntax.
(when (and (seq? predicate) ('#{fn fn*} (first predicate))) (when (and (seq? predicate) ('#{fn fn*} (first predicate)))
(throw (ex-info "match-lite :guard predicate must be an invocation form or a symbol, not a lambda" {:predicate predicate}))) (throw (ex-info "match-one :guard predicate must be an invocation form or a symbol, not a lambda" {:predicate predicate})))
(vswap! conditions conj (with-meta (if (and (seq? predicate) (not= (first predicate) 'fn*)) (vswap! conditions conj (with-meta (if (and (seq? predicate) (not= (first predicate) 'fn*))
predicate predicate
(list predicate s)) (list predicate s))
{:depends-on s}))) {:depends-on s})))
(when (:length parsed) (when (:length parsed)
(vswap! conditions conj (with-meta (list `metabase.lib.util.match.impl/count= s (:length parsed)) (vswap! conditions conj (with-meta (list `metabase.util.match.impl/count= s (:length parsed))
{:depends-on s})))) {:depends-on s}))))
:equality (vswap! conditions conj (with-meta (list `= value (:value parsed)) (meta value))) :equality (vswap! conditions conj (with-meta (list `= value (:value parsed)) (meta value)))
:set (vswap! conditions conj (list (:value parsed) value))))) :set (vswap! conditions conj (list (:value parsed) value)))))
@@ -374,7 +169,7 @@
(defn- maybe-wrap-nil [expr] (defn- maybe-wrap-nil [expr]
(if (:nil-wrapped (meta expr)) (if (:nil-wrapped (meta expr))
(vary-meta expr dissoc :nil-wrapped) (vary-meta expr dissoc :nil-wrapped)
`(metabase.lib.util.match.impl/wrap-nil ~expr))) `(metabase.util.match.impl/wrap-nil ~expr)))
(defn- expand-or-some [args] (defn- expand-or-some [args]
(case (count args) (case (count args)
@@ -391,7 +186,7 @@
common-vector-check? (some #(and (= % value-sym) (:vector-check (meta %))) common-bindings) common-vector-check? (some #(and (= % value-sym) (:vector-check (meta %))) common-bindings)
common-bindings (remove #(and (= % value-sym) (:vector-check (meta %))) common-bindings) common-bindings (remove #(and (= % value-sym) (:vector-check (meta %))) common-bindings)
value-binding (if common-vector-check? value-binding (if common-vector-check?
`(metabase.lib.util.match.impl/vector! ~(or value-binding value-sym)) `(metabase.util.match.impl/vector! ~(or value-binding value-sym))
value-binding)] value-binding)]
`(let [~@(when (and (some? value-binding) (not= value-sym value-binding)) `(let [~@(when (and (some? value-binding) (not= value-sym value-binding))
[value-sym value-binding]) [value-sym value-binding])
@@ -428,15 +223,15 @@
form)) form))
form)) form))
(defn- match-lite* [value clauses] (defn- match-one* [value clauses]
(when (odd? (count clauses)) (when (odd? (count clauses))
(throw (ex-info "match-lite requires even number of clauses" {}))) (throw (ex-info "match-one requires even number of clauses" {})))
(let [pairs (partition 2 clauses) (let [pairs (partition 2 clauses)
has-default? (= (first (last pairs)) '_) has-default? (= (first (last pairs)) '_)
[pairs default] (if has-default? [pairs default] (if has-default?
[(butlast pairs) (second (last pairs))] [(butlast pairs) (second (last pairs))]
[pairs nil]) [pairs nil])
;; match-lite is always recursive unless there is a default clause ;; match-one is always recursive unless there is a default clause
recursive? (not has-default?) recursive? (not has-default?)
;; Search for &recur and &parents usage in clauses. ;; Search for &recur and &parents usage in clauses.
contains-&recur? (contains-symbol? pairs '&recur) contains-&recur? (contains-symbol? pairs '&recur)
@@ -445,11 +240,11 @@
;; Wrap explicit nil values. ;; Wrap explicit nil values.
value (if (nil? value) `(identity nil) value) value (if (nil? value) `(identity nil) value)
value-binding (when-not (or recursive? contains-&recur?) value) value-binding (when-not (or recursive? contains-&recur?) value)
body `(metabase.lib.util.match.impl/unwrap-nil body `(metabase.util.match.impl/unwrap-nil
~(expand-or-some ~(expand-or-some
(cond-> [(process-clauses pairs '&match value-binding)] (cond-> [(process-clauses pairs '&match value-binding)]
(some? default) (conj default) (some? default) (conj default)
recursive? (conj `(metabase.lib.util.match.impl/match-lite-in-collection ~'&recur ~'&match ~'&parents)))))] recursive? (conj `(metabase.util.match.impl/match-one-in-collection ~'&recur ~'&match ~'&parents)))))]
(if (or recursive? contains-&recur?) (if (or recursive? contains-&recur?)
`((fn ~'&recur [~'&match ~'&parents] `((fn ~'&recur [~'&match ~'&parents]
~body) ~body)
@@ -458,18 +253,13 @@
~(when contains-&parents? [])) ~(when contains-&parents? []))
body))) body)))
(defmacro match-lite (defmacro match-one
"Pattern matching macro, simplified version of [[clojure.core.match]]. "Pattern matching macro, simplified version of [[clojure.core.match]].
TODO (Sashko 2026-02-01): this macro should eventually supersede all cases of `match`.
NB: unlike previously used `lib.util.match/match`, this version doesn't support using `:tag` to match `[:tag & _]`.
You should write out the vector explicitly.
Return a single thing that matches one of the match `clauses` inside `value`. If none of the clauses matched, return Return a single thing that matches one of the match `clauses` inside `value`. If none of the clauses matched, return
`nil`. Recurses through maps and sequences. A clause is a pair of a match pattern and a return expression. Usage: `nil`. Recurses through maps and sequences. A clause is a pair of a match pattern and a return expression. Usage:
(match-lite value (match-one value
pattern1 result1 pattern1 result1
pattern2 result2 pattern2 result2
...) ...)
@@ -498,23 +288,23 @@
Examples: Examples:
;; keyword pattern ;; keyword pattern
(match-lite {:fields [[:field 10 nil]]} :field) ; -> [:field 10 nil] (match-one {:fields [[:field 10 nil]]} [:field & _] &match) ; -> [:field 10 nil]
;; set of keywords ;; set of keywords
(match-lite some-query #{:field :expression}) ; -> [:field 10 nil] or [:expression \"wow\"] (match-one some-query [#{:field :expression} & _] &match) ; -> [:field 10 nil] or [:expression \"wow\"]
;; match any `:field` clause with two args (which should be all of them) ;; match any `:field` clause with two args (which should be all of them)
(match-lite some-query [:field _ _]) (match-one some-query [:field _ _] &match)
;; match-lite any `:field` clause with integer ID > 100 ;; match-one any `:field` clause with integer ID > 100
(match-lite some-query [:field (num :guard (and (integer? num) (> num 100)))]) ; -> [:field 200 nil] (match-one some-query [:field (num :guard (and (integer? num) (> num 100)))] &match) ; -> [:field 200 nil]
;; symbol naming a predicate function ;; symbol naming a predicate function
;; match anything that satisfies that predicate ;; match anything that satisfies that predicate
(match-lite some-query integer?) (match-one some-query (_ :guard integer?) &match)
;; match anything with `_` ;; match anything with `_`
(match-lite 100 `_` :anything) ; -> :anything (match-one 100 `_` :anything) ; -> :anything
The return expresion can use any of the bindings established in the pattern to compute what should be returned. The return expresion can use any of the bindings established in the pattern to compute what should be returned.
`nil` is a legal return value and prevents other clauses from being checked and matched. The following special forms `nil` is a legal return value and prevents other clauses from being checked and matched. The following special forms
@@ -527,19 +317,19 @@
Examples: Examples:
;; find vector with exactly 3 items and multiply them ;; find vector with exactly 3 items and multiply them
(match-lite [[[[[10 20 30]]]]] [_ _ _] (reduce * &match)) ; -> 6000 (match-one [[[[[10 20 30]]]]] [_ _ _] (reduce * &match)) ; -> 6000
;; returns [:div :a :href] ;; returns [:div :a :href]
(match-lite [:div [:a {:href \"hello\"}]] (match-one [:div [:a {:href \"hello\"}]]
string? &parents) string? &parents)
;; find innermost :div ;; find innermost :div
(match-lite [:div [:div [:div \"hello\"]]] (match-one [:div [:div [:div \"hello\"]]]
[:div (nested :guard vector?)] (&recur nested) [:div (nested :guard vector?)] (&recur nested)
[:div & _] &match) [:div & _] &match)
; -> [:div \"hello\"]" ; -> [:div \"hello\"]"
[value & clauses] [value & clauses]
(match-lite* value clauses)) (match-one* value clauses))
(defn- match-many* [value clauses] (defn- match-many* [value clauses]
(when (odd? (count clauses)) (when (odd? (count clauses))
@@ -549,7 +339,7 @@
[pairs default] (if has-default? [pairs default] (if has-default?
[(butlast pairs) (second (last pairs))] [(butlast pairs) (second (last pairs))]
[pairs nil]) [pairs nil])
;; match-lite is always recursive unless there is a default clause ;; match-many is always recursive unless there is a default clause
recursive? (not has-default?) recursive? (not has-default?)
;; Search for &recur and &parents usage in clauses. ;; Search for &recur and &parents usage in clauses.
contains-&recur? (contains-symbol? pairs '&recur) contains-&recur? (contains-symbol? pairs '&recur)
@@ -566,18 +356,18 @@
(some? default) (conj default)))] (some? default) (conj default)))]
;; Important: a clause may return `nil` to stop further recursive search, yet we don't want that nil to ;; Important: a clause may return `nil` to stop further recursive search, yet we don't want that nil to
;; wind up in results. ;; wind up in results.
(do (some->> (metabase.lib.util.match.impl/unwrap-nil result#) (vswap! acc# conj)) (do (some->> (metabase.util.match.impl/unwrap-nil result#) (vswap! acc# conj))
nil) nil)
(metabase.lib.util.match.impl/match-lite-in-collection ~'&recur ~'&match ~'&parents))) (metabase.util.match.impl/match-one-in-collection ~'&recur ~'&match ~'&parents)))
~value ~value
~(when contains-&parents? [])) ~(when contains-&parents? []))
(perf/not-empty @acc#)))) (perf/not-empty @acc#))))
(defmacro match-many (defmacro match-many
"Pattern matching macro, returns multiple things that match one of the `clauses` inside `value. See `match-lite` for "Pattern matching macro, returns multiple things that match one of the `clauses` inside `value. See `match-one` for
pattern and return expression syntax. pattern and return expression syntax.
There are several important characteristics that make `match-many` behavior semantically different from `match-lite`: There are several important characteristics that make `match-many` behavior semantically different from `match-one`:
1. If one or more clauses matched, return a vector of the matched return values. If none of the clauses were 1. If one or more clauses matched, return a vector of the matched return values. If none of the clauses were
matched, `nil` is returned instead of `[]`. matched, `nil` is returned instead of `[]`.
@@ -594,48 +384,38 @@
[value & clauses] [value & clauses]
(match-many* value clauses)) (match-many* value clauses))
(defmacro replace* ;; TODO - it would be ultra handy to have a `match-all` function that could handle clauses with recursive matches,
"Internal implementation for `replace`. Generate a pattern-matching function with `core.match`, and use it to replace ;; e.g. with a query like
matching values in `form`." ;;
[form patterns-and-results] ;; {:query {:source-table 1, :joins [{:source-table 2, ...}]}}
(let [replace-fn-symb (gensym "replace-")] ;;
`((fn ~replace-fn-symb [~'&parents ~'&match] ;; it would be useful to be able to do
(match** [~'&match] ;;
~@(generate-patterns-and-results replace-fn-symb patterns-and-results, :wrap-result-forms? false) ;;
~@(when-not (skip-else-clause? patterns-and-results) ;; ;; get *all* the source tables
[:else `(metabase.lib.util.match.impl/replace-in-collection ~replace-fn-symb ~'&parents ~'&match)]))) ;; (match/match-all query
[] ;; (&match :guard (every-pred map? :source-table))
~form))) ;; (:source-table &match))
(defmacro replace (defmacro replace
"Like `match`, but replace matches in `x` with the results of result body. The same pattern options are supported, "Walk `form` recursively and replace all patterns matched with `match-one` by the respective return expressions. The
and `&parents` and `&match` anaphors are available in the same way. (`&match` is particularly useful here if you
want to use keywords or sets of keywords as patterns.)"
{:style/indent :defn}
[x & patterns-and-results]
;; as with `match` actual impl is in `match` namespace to discourage you from using the constituent functions and
;; macros that power this macro directly
`(replace* ~x ~patterns-and-results))
(defmacro replace-lite
"Walk `form` recursively and replace all patterns matched with `match-lite` by the respective return expressions. The
same pattern options are supported, and `&parents` and `&match` anaphors are available in the same way." same pattern options are supported, and `&parents` and `&match` anaphors are available in the same way."
[form & clauses] [form & clauses]
(let [replace-fn-symb (gensym "replace-") (let [replace-fn-symb (gensym "replace-")
contains-&parents? (contains-symbol? clauses '&parents)] contains-&parents? (contains-symbol? clauses '&parents)]
`((fn ~replace-fn-symb [~'&match ~'&parents] `((fn ~replace-fn-symb [~'&match ~'&parents]
(match-lite ~'&match (match-one ~'&match
~@clauses ~@clauses
~'_ (metabase.lib.util.match.impl/replace-lite-in-collection ~'_ (metabase.util.match.impl/replace-in-collection
~replace-fn-symb ~'&match ~(when contains-&parents? ~replace-fn-symb ~'&match ~(when contains-&parents?
'&parents)))) '&parents))))
~form ~form
~(when contains-&parents? [])))) ~(when contains-&parents? []))))
(defmacro replace-in (defmacro replace-in
"Like `replace-lite`, but only replaces things in the part of `x` in the keypath `ks` (i.e. the way to `update-in` works.)" "Like `replace`, but only replaces things in the part of `x` in the keypath `ks` (i.e. the way to `update-in` works.)"
{:style/indent :defn} {:style/indent :defn}
[x ks & patterns-and-results] [x ks & patterns-and-results]
`(metabase.lib.util.match.impl/update-in-unless-empty ~x ~ks (fn [x#] (replace-lite x# ~@patterns-and-results)))) `(metabase.util.match.impl/update-in-unless-empty ~x ~ks (fn [x#] (replace x# ~@patterns-and-results))))
;; TODO - it would be useful to have something like a `replace-all` function as well ;; TODO - it would be useful to have something like a `replace-all` function as well

View File

@@ -0,0 +1,7 @@
(ns metabase.util.match
(:require
[metabase.util.match.impl])
(:require-macros [metabase.util.match]))
(comment metabase.util.match/keep-me
metabase.util.match.impl/keep-me)

View File

@@ -0,0 +1,84 @@
(ns metabase.util.match.impl
"Internal implementation of the MBQL `match` and `replace` macros. Don't use these directly."
(:refer-clojure :exclude [mapv get-in empty?])
(:require
[metabase.util.performance :refer [mapv get-in empty?]]))
(defn update-in-unless-empty
"Like `update-in`, but only updates if the existing value is non-empty."
[m ks f]
(if (empty? (get-in m ks))
m
(update-in m ks f)))
(defn vector!
"Return nil if `obj` is not a vector, otherwise return `obj`."
[obj]
(when (vector? obj) obj))
(defn map!
"Return nil if `obj` is not a map, otherwise return `obj`."
[obj]
(when (map? obj) obj))
(defn count=
"Return true if collection `coll` has precisely `cnt` elements in it."
[coll cnt]
(= (count coll) cnt))
(defn count>=
"Return true if collection `coll` has `cnt` elements or more."
[coll cnt]
(>= (count coll) cnt))
(defn wrap-nil
"If `value` is nil, return `::wrapped-nil`, otherwise return `value`."
[value]
(if (nil? value) ::wrapped-nil value))
(defn unwrap-nil
"If `value` is `::wrapped-nil`, return nil, otherwise return `value`."
[value]
(when-not (= value ::wrapped-nil) value))
(defn- maybe-add-to-parents [clause-parents k]
(if (and clause-parents k)
(conj clause-parents k)
clause-parents))
(defn match-one-in-collection
"Internal impl for `match-one`. If `form` is a collection, call `match-fn` to recursively look for matches in it.
`clause-parents` is a sequence of keywords naming the parent top-level keys and clauses of the match."
[match-fn form clause-parents]
{:pre [(fn? match-fn)]}
(cond
(map? form)
(reduce-kv (fn [_ k v]
(when-some [match (match-fn v (maybe-add-to-parents clause-parents k))]
(reduced match)))
nil form)
(sequential? form)
(reduce (fn [_ v]
(let [fst (first form)]
(when-some [match (match-fn v (maybe-add-to-parents clause-parents (when (keyword? fst) fst)))]
(reduced match))))
nil form)))
(defn replace-in-collection
"Internal impl for `replace`. Recursively replace values in a collection using a `replace-fn`."
[replace-fn form clause-parents]
(cond
(map? form)
(reduce-kv (fn [form k v]
(let [repl (replace-fn v (maybe-add-to-parents clause-parents k))]
(cond-> form
(not (identical? v form)) (assoc k repl))))
form form)
(sequential? form)
(mapv #(let [fst (first form)]
(replace-fn % (maybe-add-to-parents clause-parents (when (keyword? fst) fst))))
form)
:else form))

View File

@@ -1,6 +1,5 @@
(ns metabase.warehouses.models.database (ns metabase.warehouses.models.database
(:require (:require
[clojure.core.match :refer [match]]
[clojure.data :as data] [clojure.data :as data]
[medley.core :as m] [medley.core :as m]
[metabase.analytics-interface.core :as analytics] [metabase.analytics-interface.core :as analytics]
@@ -177,26 +176,27 @@
(defn- infer-db-schedules (defn- infer-db-schedules
"Infer database schedule settings based on its options." "Infer database schedule settings based on its options."
[{:keys [details is_full_sync is_on_demand cache_field_values_schedule metadata_sync_schedule] :as database}] [{:keys [details is_full_sync is_on_demand cache_field_values_schedule metadata_sync_schedule] :as database}]
(match [(boolean (:let-user-control-scheduling details)) is_full_sync is_on_demand] (let [user-control-scheduling (boolean (:let-user-control-scheduling details))]
[false _ _] (cond (not user-control-scheduling)
(merge (merge
database database
(sync.schedules/schedule-map->cron-strings (sync.schedules/schedule-map->cron-strings
(sync.schedules/default-randomized-schedule))) (sync.schedules/default-randomized-schedule)))
;; "Regularly on a schedule" (and user-control-scheduling is_full_sync (not is_on_demand))
;; -> sync both steps, schedule should be provided ;; "Regularly on a schedule"
[true true false] ;; -> sync both steps, schedule should be provided
(do (do
(assert (every? some? [cache_field_values_schedule metadata_sync_schedule])) (assert (every? some? [cache_field_values_schedule metadata_sync_schedule]))
database) database)
;; "Only when adding a new filter" or "Never, I'll do it myself" (and user-control-scheduling (not is_full_sync))
;; -> Sync metadata only ;; schedules should only contains metadata_sync, but FE might sending both
[true false _] ;; so we just manually nullify it here
;; schedules should only contains metadata_sync, but FE might sending both (assoc database :cache_field_values_schedule nil)
;; so we just manually nullify it here
(assoc database :cache_field_values_schedule nil))) :else (throw (ex-info "Illegal options combination."
(select-keys database [:let-user-control-scheduling :is_full_sync :is_on_demand]))))))
(defn is-destination? (defn is-destination?
"Is this database a destination database for some router database?" "Is this database a destination database for some router database?"

View File

@@ -20,7 +20,6 @@
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata :as lib.metadata]
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.util.match :as lib.util.match]
[metabase.models.interface :as mi] [metabase.models.interface :as mi]
[metabase.permissions.core :as perms] [metabase.permissions.core :as perms]
[metabase.premium-features.core :as premium-features :refer [defenterprise]] [metabase.premium-features.core :as premium-features :refer [defenterprise]]
@@ -41,6 +40,7 @@
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.malli.schema :as ms] [metabase.util.malli.schema :as ms]
[metabase.util.match :as match]
[metabase.util.quick-task :as quick-task] [metabase.util.quick-task :as quick-task]
[metabase.warehouse-schema.models.field :refer [readable-fields-only]] [metabase.warehouse-schema.models.field :refer [readable-fields-only]]
[metabase.warehouse-schema.table :as schema.table] [metabase.warehouse-schema.table :as schema.table]
@@ -154,7 +154,7 @@
we can filter it out in Clojure-land." we can filter it out in Clojure-land."
[{query :dataset_query, :as _card} :- [:map [{query :dataset_query, :as _card} :- [:map
[:dataset_query ::queries.schema/query]]] [:dataset_query ::queries.schema/query]]]
(lib.util.match/match-lite (lib/aggregations query) [#{:cum-count :cum-sum} & _] true)) (match/match-one (lib/aggregations query) [#{:cum-count :cum-sum} & _] true))
(defn card-can-be-used-as-source-query? (defn card-can-be-used-as-source-query?
"Does `card`'s query meet the conditions required for it to be used as a source query for another query?" "Does `card`'s query meet the conditions required for it to be used as a source query for another query?"

View File

@@ -6,13 +6,13 @@
[metabase.analyze.core :as analyze] [metabase.analyze.core :as analyze]
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.util.match :as lib.util.match]
[metabase.models.interface :as mi] [metabase.models.interface :as mi]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.json :as json] [metabase.util.json :as json]
[metabase.util.log :as log] [metabase.util.log :as log]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms] [metabase.util.malli.schema :as ms]
[metabase.util.match :as match]
[metabase.xrays.automagic-dashboards.schema :as ads] [metabase.xrays.automagic-dashboards.schema :as ads]
[ring.util.codec :as codec] [ring.util.codec :as codec]
[toucan2.core :as t2])) [toucan2.core :as t2]))
@@ -61,12 +61,12 @@
(mu/defn field-reference->id :- [:maybe [:or ms/NonBlankString ::lib.schema.id/field]] (mu/defn field-reference->id :- [:maybe [:or ms/NonBlankString ::lib.schema.id/field]]
"Extract field ID from a given field reference form." "Extract field ID from a given field reference form."
[clause :- :mbql.clause/field] [clause :- :mbql.clause/field]
(lib.util.match/match-lite clause [:field _opts id] id)) (match/match-one clause [:field _opts id] id))
(mu/defn collect-field-references :- [:maybe [:sequential :mbql.clause/field]] (mu/defn collect-field-references :- [:maybe [:sequential :mbql.clause/field]]
"Collect all `:field` references from a given form." "Collect all `:field` references from a given form."
[form] [form]
(lib.util.match/match-many form [:field & _] &match)) (match/match-many form [:field & _] &match))
(mu/defn ->field :- [:maybe [:and (mu/defn ->field :- [:maybe [:and
(ms/InstanceOf :model/Field) (ms/InstanceOf :model/Field)

View File

@@ -5,10 +5,10 @@
;; legacy usage, do not use legacy MBQL stuff in new code. ;; legacy usage, do not use legacy MBQL stuff in new code.
^{:clj-kondo/ignore [:deprecated-namespace :discouraged-namespace]} [metabase.legacy-mbql.util :as mbql.u] ^{:clj-kondo/ignore [:deprecated-namespace :discouraged-namespace]} [metabase.legacy-mbql.util :as mbql.u]
[metabase.lib.schema.common :as lib.schema.common] [metabase.lib.schema.common :as lib.schema.common]
[metabase.lib.util.match :as lib.util.match]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms] [metabase.util.malli.schema :as ms]
[metabase.util.match :as match]
[metabase.xrays.domain-entities.specs :as domain-entities.specs :refer [*domain-entity-specs* MBQL]] [metabase.xrays.domain-entities.specs :as domain-entities.specs :refer [*domain-entity-specs* MBQL]]
[toucan2.core :as t2])) [toucan2.core :as t2]))
@@ -61,7 +61,7 @@
[bindings :- Bindings [bindings :- Bindings
source :- SourceName source :- SourceName
obj] obj]
(lib.util.match/replace-lite obj (match/replace obj
[:dimension dimension] (->> dimension [:dimension dimension] (->> dimension
(get-dimension-binding bindings source) (get-dimension-binding bindings source)
(resolve-dimension-clauses bindings source)))) (resolve-dimension-clauses bindings source))))
@@ -101,7 +101,7 @@
(into (empty entities) ; this way we don't care if we're dealing with a map or a vec (into (empty entities) ; this way we don't care if we're dealing with a map or a vec
(for [entity entities (for [entity entities
:when (every? (get-in bindings [source :dimensions]) :when (every? (get-in bindings [source :dimensions])
(lib.util.match/match-many entity [:dimension dimension] dimension))] (match/match-many entity [:dimension dimension] dimension))]
(resolve-dimension-clauses bindings source entity)))) (resolve-dimension-clauses bindings source entity))))
(mu/defn- instantiate-domain-entity :- ::domain-entities.specs/instantiated-domain-entity (mu/defn- instantiate-domain-entity :- ::domain-entities.specs/instantiated-domain-entity

View File

@@ -3,13 +3,13 @@
[medley.core :as m] [medley.core :as m]
[metabase.driver :as driver] [metabase.driver :as driver]
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.util.match :as lib.util.match]
[metabase.models.interface :as mi] [metabase.models.interface :as mi]
[metabase.query-processor.preprocess :as qp.preprocess] [metabase.query-processor.preprocess :as qp.preprocess]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.i18n :refer [tru]] [metabase.util.i18n :refer [tru]]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.schema :as ms] [metabase.util.malli.schema :as ms]
[metabase.util.match :as match]
[metabase.warehouse-schema.models.table :as table] [metabase.warehouse-schema.models.table :as table]
[metabase.xrays.domain-entities.core :as de :refer [Bindings DimensionBindings SourceEntity SourceName]] [metabase.xrays.domain-entities.core :as de :refer [Bindings DimensionBindings SourceEntity SourceName]]
[metabase.xrays.domain-entities.specs :as domain-entities.specs :refer [*domain-entity-specs* DomainEntitySpec]] [metabase.xrays.domain-entities.specs :as domain-entities.specs :refer [*domain-entity-specs* DomainEntitySpec]]
@@ -30,7 +30,7 @@
(defn- mbql-reference->col-name (defn- mbql-reference->col-name
[field-clause] [field-clause]
(lib.util.match/match-lite field-clause (match/match-one field-clause
[:field (field-name :guard string?) _] [:field (field-name :guard string?) _]
field-name field-name

View File

@@ -7,9 +7,9 @@
;; legacy usages, do not use legacy MBQL stuff in new code. ;; legacy usages, do not use legacy MBQL stuff in new code.
^{:clj-kondo/ignore [:discouraged-namespace]} [metabase.legacy-mbql.schema :as mbql.s] ^{:clj-kondo/ignore [:discouraged-namespace]} [metabase.legacy-mbql.schema :as mbql.s]
[metabase.lib.schema.join :as lib.schema.join] [metabase.lib.schema.join :as lib.schema.join]
[metabase.lib.util.match :as lib.util.match]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.util.yaml :as yaml] [metabase.util.yaml :as yaml]
[metabase.xrays.domain-entities.specs :as domain-entities.specs :refer [MBQL]])) [metabase.xrays.domain-entities.specs :as domain-entities.specs :refer [MBQL]]))
@@ -40,7 +40,7 @@
(defn- extract-dimensions (defn- extract-dimensions
[mbql] [mbql]
(lib.util.match/match-many (domain-entities.specs/normalize-mbql-clause mbql) (match/match-many (domain-entities.specs/normalize-mbql-clause mbql)
[:dimension dimension & _] dimension)) [:dimension dimension & _] dimension))
(def ^:private ^{:arglists '([m])} stringify-keys (def ^:private ^{:arglists '([m])} stringify-keys

View File

@@ -6,11 +6,11 @@
[hickory.select :as hik.s] [hickory.select :as hik.s]
[metabase.channel.render.card :as channel.render.card] [metabase.channel.render.card :as channel.render.card]
[metabase.channel.render.core :as channel.render] [metabase.channel.render.core :as channel.render]
[metabase.lib.util.match :as lib.util.match]
[metabase.pulse.render.test-util :as render.tu] [metabase.pulse.render.test-util :as render.tu]
[metabase.query-processor.test :as qp] [metabase.query-processor.test :as qp]
[metabase.test :as mt] [metabase.test :as mt]
[metabase.util :as u])) [metabase.util :as u]
[metabase.util.match :as match]))
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
@@ -39,7 +39,7 @@
:display :line :display :line
:visualization_settings {:graph.dimensions ["CREATED_AT"] :visualization_settings {:graph.dimensions ["CREATED_AT"]
:graph.metrics ["count"]}}] :graph.metrics ["count"]}}]
(is (some? (lib.util.match/match-lite (is (some? (match/match-one
(render-pulse-card card) (render-pulse-card card)
[:img _] true))))))) [:img _] true)))))))
@@ -293,7 +293,7 @@
nil nil
(qp/process-query (:dataset_query card)) (qp/process-query (:dataset_query card))
{:channel.render/include-title? true}))] {:channel.render/include-title? true}))]
(is (lib.util.match/match-lite rendered-card-content (is (match/match-one rendered-card-content
[:a {:href (href :guard (= href (format "https://mb.com/question/%d" (:id card))))} "A Card"] [:a {:href (href :guard (= href (format "https://mb.com/question/%d" (:id card))))} "A Card"]
true))))))) true)))))))
@@ -311,7 +311,7 @@
(qp/process-query (:dataset_query card)) (qp/process-query (:dataset_query card))
{:channel.render/include-title? true})) {:channel.render/include-title? true}))
expected-href (format "https://mb.com/dashboard/%d#scrollTo=%d" (:dashboard_id dc1) (:id dc1))] expected-href (format "https://mb.com/dashboard/%d#scrollTo=%d" (:dashboard_id dc1) (:id dc1))]
(is (every? #(= % expected-href) (lib.util.match/match-many rendered-card-content {:href href} href))))))) (is (every? #(= % expected-href) (match/match-many rendered-card-content {:href href} href)))))))
(testing "the title and body hrefs for visualizer cards should be of the form '.../dashboard/<DASHBOARD_ID>#scrollTo=<DASHBOARD_CARD_ID>'" (testing "the title and body hrefs for visualizer cards should be of the form '.../dashboard/<DASHBOARD_ID>#scrollTo=<DASHBOARD_CARD_ID>'"
(mt/with-temp [:model/Card card {:name "A Card" (mt/with-temp [:model/Card card {:name "A Card"
:dataset_query (mt/mbql-query venues {:limit 1})} :dataset_query (mt/mbql-query venues {:limit 1})}
@@ -325,7 +325,7 @@
(qp/process-query (:dataset_query card)) (qp/process-query (:dataset_query card))
{:channel.render/include-title? true})) {:channel.render/include-title? true}))
expected-href (format "https://mb.com/dashboard/%d#scrollTo=%d" (:dashboard_id dc1) (:id dc1))] expected-href (format "https://mb.com/dashboard/%d#scrollTo=%d" (:dashboard_id dc1) (:id dc1))]
(is (every? #(= % expected-href) (lib.util.match/match-many rendered-card-content {:href href} href)))))))) (is (every? #(= % expected-href) (match/match-many rendered-card-content {:href href} href))))))))
(deftest render-card-with-abbreviated-dates-test (deftest render-card-with-abbreviated-dates-test
(testing "Static-viz should render without error when date formatting is abbreviated (metabase#27020)" (testing "Static-viz should render without error when date formatting is abbreviated (metabase#27020)"

View File

@@ -6,10 +6,10 @@
[metabase.lib.core :as lib] [metabase.lib.core :as lib]
[metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata :as lib.metadata]
[metabase.lib.test-metadata :as meta] [metabase.lib.test-metadata :as meta]
[metabase.lib.util.match :as lib.util.match]
[metabase.lib.walk :as lib.walk] [metabase.lib.walk :as lib.walk]
[metabase.util :as u] [metabase.util :as u]
[metabase.util.malli.registry :as mr])) [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]))
#?(:cljs (comment metabase.test-runner.assert-exprs.approximately-equal/keep-me)) #?(:cljs (comment metabase.test-runner.assert-exprs.approximately-equal/keep-me))
@@ -284,7 +284,7 @@
(lib.walk/walk-clauses (lib.walk/walk-clauses
query query
(fn [query _path-type _stage-or-join-path clause] (fn [query _path-type _stage-or-join-path clause]
(lib.util.match/match-lite clause (match/match-one clause
[:field opts id] [:field opts id]
(let [col (lib.metadata/field query id)] (let [col (lib.metadata/field query id)]
[:field (merge (select-keys col [:base-type]) opts) (:name col)]) [:field (merge (select-keys col [:base-type]) opts) (:name col)])

View File

@@ -15,11 +15,11 @@
[metabase.lib.test-util.macros :as lib.tu.macros] [metabase.lib.test-util.macros :as lib.tu.macros]
[metabase.lib.test-util.metadata-providers.mock :as providers.mock] [metabase.lib.test-util.metadata-providers.mock :as providers.mock]
[metabase.lib.test-util.uuid-dogs-metadata-provider :as lib.tu.uuid-dogs-metadata-provider] [metabase.lib.test-util.uuid-dogs-metadata-provider :as lib.tu.uuid-dogs-metadata-provider]
[metabase.lib.util.match :as lib.util.match]
[metabase.query-processor.preprocess :as qp.preprocess] [metabase.query-processor.preprocess :as qp.preprocess]
^{:clj-kondo/ignore [:deprecated-namespace]} [metabase.query-processor.store :as qp.store] ^{:clj-kondo/ignore [:deprecated-namespace]} [metabase.query-processor.store :as qp.store]
[metabase.query-processor.util.add-alias-info :as add] [metabase.query-processor.util.add-alias-info :as add]
[metabase.test :as mt])) [metabase.test :as mt]
[metabase.util.match :as match]))
(comment h2/keep-me) (comment h2/keep-me)
@@ -1098,9 +1098,9 @@
:condition [:= $product-id &PRODUCTS__via__PRODUCT_ID.products.id]}]}))] :condition [:= $product-id &PRODUCTS__via__PRODUCT_ID.products.id]}]}))]
(is (=? [[:expression {::add/source-table ::add/none, ::add/desired-alias "pivot-grouping"} "pivot-grouping"] (is (=? [[:expression {::add/source-table ::add/none, ::add/desired-alias "pivot-grouping"} "pivot-grouping"]
[:expression {::add/source-table ::add/none, ::add/desired-alias "pivot-grouping"} "pivot-grouping"]] [:expression {::add/source-table ::add/none, ::add/desired-alias "pivot-grouping"} "pivot-grouping"]]
(lib.util.match/match-many (-> query (match/match-many (-> query
add/add-alias-info add/add-alias-info
qp.preprocess/preprocess) qp.preprocess/preprocess)
[:expression & _] &match)))))) [:expression & _] &match))))))
(deftest ^:parallel remapped-columns-in-joined-source-queries-test (deftest ^:parallel remapped-columns-in-joined-source-queries-test

View File

@@ -1,51 +1,30 @@
(ns metabase.lib.util.match-test (ns metabase.util.match-test
{:clj-kondo/config '{:linters
{:discouraged-var {metabase.lib.util.match/match {:level :off}
metabase.lib.util.match/match-one {:level :off}
metabase.lib.util.match/replace {:level :off}}}}}
(:require (:require
[clojure.test :as t] [clojure.test :as t]
[metabase.lib.util.match :as lib.util.match])) [metabase.util.match :as match]))
(t/deftest ^:parallel basic-match-test (t/deftest ^:parallel basic-match-test
(t/testing "can we use `match` to find the instances of a clause?" (t/testing "can we use `match-many` to find the instances of a clause?"
(t/is (= [[:field 10 nil] (t/is (= [[:field 10 nil]
[:field 20 nil]] [:field 20 nil]]
(lib.util.match/match {:query {:filter [:= (match/match-many {:query {:filter [:=
[:field 10 nil] [:field 10 nil]
[:field 20 nil]]}} [:field 20 nil]]}}
[:field & _]))))) [:field & _] &match)))))
(t/deftest ^:parallel match-keywords-test
(t/testing "is `match` nice enought to automatically wrap raw keywords in appropriate patterns for us?"
(t/is (= [[:field 1 nil]]
(lib.util.match/match {:fields [[:field 1 nil] [:expression "wow"]]}
:field)))))
(t/deftest ^:parallel match-set-of-keywords-tes
(t/testing "if we pass a set of keywords, will that generate an appropriate pattern to match multiple clauses as well?"
(t/is (= [[:field 1 nil]
[:field 3 {:source-field 2}]
[:expression "wow"]]
(lib.util.match/match {:fields [[:field 1 nil]
[:something-else "ok"]
[:field 3 {:source-field 2}]
[:expression "wow"]]}
#{:field :expression})))))
(t/deftest ^:parallel match-dont-include-subclauses-test (t/deftest ^:parallel match-dont-include-subclauses-test
(t/testing "`match` shouldn't include subclauses of matches" (t/testing "`match-many` shouldn't include subclauses of matches"
(t/is (= [[:field 1 nil] (t/is (= [[:field 1 nil]
[:field 3 {:source-field 2}]] [:field 3 {:source-field 2}]]
(lib.util.match/match [[:field 1 nil] [:field 3 {:source-field 2}]] (match/match-many [[:field 1 nil] [:field 3 {:source-field 2}]]
[(:or :field) & _]))) [(:or :field) & _] &match)))
(t/is (= [[:field 10 nil] (t/is (= [[:field 10 nil]
[:field 20 nil]] [:field 20 nil]]
(lib.util.match/match {:query {:filter [:= (match/match-many {:query {:filter [:=
[:field 10 nil] [:field 10 nil]
[:field 20 nil]]}} [:field 20 nil]]}}
[(:or :field :+ :-) & _]))))) [(:or :field :+ :-) & _] &match)))))
;; can we use some of the cool features of pattern matching? ;; can we use some of the cool features of pattern matching?
(def ^:private a-query (def ^:private a-query
@@ -58,27 +37,27 @@
(t/testing "can we use the optional `result` parameter to find return something other than the whole clause?" (t/testing "can we use the optional `result` parameter to find return something other than the whole clause?"
(t/is (= [41] (t/is (= [41]
;; return just the dest IDs of Fields in a fk-> clause ;; return just the dest IDs of Fields in a fk-> clause
(lib.util.match/match a-query (match/match-many a-query
[:field dest-id {:source-field (_ :guard integer?)}] (inc dest-id)))) [:field dest-id {:source-field (_ :guard integer?)}] (inc dest-id))))
(t/is (= [10 20] (t/is (= [10 20]
(lib.util.match/match (:breakout a-query) [:field id nil] id))))) (match/match-many (:breakout a-query) [:field id nil] id)))))
(t/deftest ^:parallel match-return-nil-for-empty-sequences-test (t/deftest ^:parallel match-return-nil-for-empty-sequences-test
(t/testing "match should return `nil` if there are no matches so you don't need to call `seq`" (t/testing "match should return `nil` if there are no matches so you don't need to call `seq`"
(t/is (= nil (t/is (= nil
(lib.util.match/match {} [:field _ _] :minute))))) (match/match-many {} [:field _ _] :minute)))))
(t/deftest ^:parallel match-guard-test (t/deftest ^:parallel match-guard-test
(t/testing "can we `:guard` a pattern?" (t/testing "can we `:guard` a pattern?"
(t/is (= [[:field 2 nil]] (t/is (= [[:field 2 nil]]
(let [a-field-id 2] (let [a-field-id 2]
(lib.util.match/match {:fields [[:field 1 nil] [:field 2 nil]]} (match/match-many {:fields [[:field 1 nil] [:field 2 nil]]}
[:field (id :guard (partial = a-field-id)) _]))))) [:field (id :guard (= id a-field-id)) _] &match)))))
(t/testing "ok, if for some reason we can't use `:guard` in the pattern will `match` filter out nil results?" (t/testing "ok, if for some reason we can't use `:guard` in the pattern will `match-many` filter out nil results?"
(t/is (= [2] (t/is (= [2]
(lib.util.match/match {:fields [[:field 1 nil] [:field 2 nil]]} (match/match-many {:fields [[:field 1 nil] [:field 2 nil]]}
[:field id _] [:field id _]
(when (= id 2) (when (= id 2)
id)))))) id))))))
@@ -95,53 +74,34 @@
[:field 2 {:temporal-unit :day}] [:field 2 {:temporal-unit :day}]
[:field 4 {:source-field 3, :temporal-unit :month}]] [:field 4 {:source-field 3, :temporal-unit :month}]]
(let [some-pred? (constantly true)] (let [some-pred? (constantly true)]
(lib.util.match/match another-query (match/match-many another-query
:field [:field & _]
(when (some-pred? &match) (when (some-pred? &match)
&match))))))) &match)))))))
(t/deftest ^:parallel match-&parents-test (t/deftest ^:parallel match-&parents-test
(t/testing "can we use the anaphoric `&parents` symbol to examine the parents of the collection?" (t/testing "can we use the anaphoric `&parents` symbol to examine the parents of the collection?"
(t/is (= [[:field 1 nil]] (t/is (= [[:field 1 nil]]
(lib.util.match/match {:filter [[:time-interval [:field 1 nil] :current :month] (match/match-many {:filter [[:time-interval [:field 1 nil] :current :month]
[:= [:field 2 nil] "wow"]]} [:= [:field 2 nil] "wow"]]}
:field [:field & _]
(when (contains? (set &parents) :time-interval) (when (contains? (set &parents) :time-interval)
&match)))))) &match))))))
#?(:clj
(t/deftest ^:parallel match-by-class-test
(t/testing "can we match using a CLASS?"
(t/is (= [#inst "2018-10-08T00:00:00.000-00:00"]
(lib.util.match/match [[:field 1 nil]
[:field 2 nil]
#inst "2018-10-08"
4000]
java.util.Date))))))
(t/deftest ^:parallel match-by-predicate-test (t/deftest ^:parallel match-by-predicate-test
(t/testing "can we match using a PREDICATE?"
(t/is (= [4000 5000]
;; find the integer args to `:=` clauses that are not inside `:field-id` clauses
(lib.util.match/match {:filter [:and
[:= [:field 1 nil] 4000]
[:= [:field 2 nil] 5000]]}
integer?
(when (= := (last &parents))
&match)))))
(t/testing "how can we use predicates not named by a symbol?" (t/testing "how can we use predicates not named by a symbol?"
(t/is (= [1 4000 2 5000] (t/is (= [1 4000 2 5000]
(lib.util.match/match {:filter [:and (match/match-many {:filter [:and
[:= [:field 1 nil] 4000] [:= [:field 1 nil] 4000]
[:= [:field 2 nil] 5000]]} [:= [:field 2 nil] 5000]]}
(&match :guard integer?))))) (&match :guard integer?)
&match))))
(t/testing "can we use a predicate and bind the match at the same time?" (t/testing "can we use a predicate and bind the match at the same time?"
(t/is (= [2 4001 3 5001] (t/is (= [2 4001 3 5001]
(lib.util.match/match {:filter [:and (match/match-many {:filter [:and
[:= [:field 1 nil] 4000] [:= [:field 1 nil] 4000]
[:= [:field 2 nil] 5000]]} [:= [:field 2 nil] 5000]]}
(i :guard integer?) (i :guard integer?)
(inc i)))))) (inc i))))))
@@ -149,39 +109,51 @@
(t/testing "can we match against a map?" (t/testing "can we match against a map?"
(t/is (= ["card__1847"] (t/is (= ["card__1847"]
(let [x {:source-table "card__1847"}] (let [x {:source-table "card__1847"}]
(lib.util.match/match x (match/match-many x
(m :guard (every-pred map? (comp string? :source-table))) (m :guard (and (map? m) (string? (:source-table m))))
(:source-table m))))))) (:source-table m)))))
(t/is (= ["card__1847"]
(let [x {:source-table "card__1847"}]
(match/match-many x
{:source-table (s :guard string?)}
s))))))
(t/deftest ^:parallel match-sequence-of-maps-test (t/deftest ^:parallel match-sequence-of-maps-test
(t/testing "how about a sequence of maps?" (t/testing "how about a sequence of maps?"
(t/is (= ["card__1847"] (t/is (= ["card__1847"]
(let [x [{:source-table "card__1847"}]] (let [x [{:source-table "card__1847"}]]
(lib.util.match/match x (match/match-many x
(m :guard (every-pred map? (comp string? :source-table))) (m :guard (and (map? m) (string? (:source-table m))))
(:source-table m))))))) (:source-table m)))))
(t/is (= ["card__1847"]
(let [x [{:source-table "card__1847"}]]
(match/match-many x
{:source-table (s :guard string?)}
s))))))
(t/deftest ^:parallel match-recur-inside-pattern-test (t/deftest ^:parallel match-recur-inside-pattern-test
(t/testing "can we use `recur` inside a pattern?" (t/testing "can we use `recur` inside a pattern?"
(t/is (= [[0 :month]] (t/is (= [[0 :month]]
(lib.util.match/match {:filter [:time-interval [:field 1 nil] :current :month]} (match/match-many {:filter [:time-interval [:field 1 nil] :current :month]}
[:time-interval field :current unit] (recur [:time-interval field 0 unit]) [:time-interval field :current unit] (&recur [:time-interval field 0 unit])
[:time-interval _ n unit] [n unit]))))) [:time-interval _ n unit] [n unit])))))
(t/deftest ^:parallel match-short-circut-test (t/deftest ^:parallel match-short-circut-test
(t/testing "can we short-circut a match to prevent recursive matching?" (t/testing "can we short-circut a match to prevent recursive matching?"
(t/is (= [10] (t/is (= [10]
(lib.util.match/match [[:field 10 nil] (match/match-many [[:field 10 nil]
[:field 20 {:temporal-unit :day}]] [:field 20 {:temporal-unit :day}]]
[:field id nil] id [:field id nil] id
[_ [:field-id & _] & _] nil))))) [_ [:field-id & _] & _] nil)))))
(t/deftest ^:parallel match-list-with-guard-clause-test (t/deftest ^:parallel match-list-with-guard-clause-test
(t/testing "can we use a list with a :guard clause?" (t/testing "can we use a list with a :guard clause?"
(t/is (= [10 20] (t/is (= [10 20]
(lib.util.match/match {:query {:filter [:= (match/match-many {:query {:filter [:=
[:field 10 nil] [:field 10 nil]
[:field 20 nil]]}} [:field 20 nil]]}}
(id :guard int?) id))))) (id :guard int?) id)))))
(t/deftest ^:parallel basic-replace-test (t/deftest ^:parallel basic-replace-test
@@ -190,7 +162,7 @@
[:field 20 {:temporal-unit :day}] [:field 20 {:temporal-unit :day}]
[:field "Wow" {:base-type :type/*}]] [:field "Wow" {:base-type :type/*}]]
:fields [[:field 40 {:source-field 30}]]} :fields [[:field 40 {:source-field 30}]]}
(lib.util.match/replace a-query (match/replace a-query
[:field id nil] [:field id nil]
[:field id {:temporal-unit :day}]))))) [:field id {:temporal-unit :day}])))))
@@ -200,7 +172,7 @@
[:field 20 {:temporal-unit :day}] [:field 20 {:temporal-unit :day}]
[:field "Wow" {:base-type :type/*}]] [:field "Wow" {:base-type :type/*}]]
:fields [[:field 40 {:source-field 30}]]} :fields [[:field 40 {:source-field 30}]]}
(lib.util.match/replace-in a-query [:breakout] (match/replace-in a-query [:breakout]
[:field (id :guard integer?) nil] [:field (id :guard integer?) nil]
[:field id {:temporal-unit :day}]))))) [:field id {:temporal-unit :day}])))))
@@ -210,7 +182,7 @@
[:field 20 {:temporal-unit :day}] [:field 20 {:temporal-unit :day}]
[:field "Wow" {:base-type :type/*, :temporal-unit :month}]] [:field "Wow" {:base-type :type/*, :temporal-unit :month}]]
:fields [[:field 40 {:source-field 30}]]} :fields [[:field 40 {:source-field 30}]]}
(lib.util.match/replace-in a-query [:breakout] (match/replace-in a-query [:breakout]
[:field (id :guard integer?) nil] [:field (id :guard integer?) nil]
[:field id {:temporal-unit :day}] [:field id {:temporal-unit :day}]
@@ -223,7 +195,7 @@
[:field 20 nil] [:field 20 nil]
[:field "Wow" {:base-type :type/*}]] [:field "Wow" {:base-type :type/*}]]
:fields [[:field 100 {:source-field 30}]]} :fields [[:field 100 {:source-field 30}]]}
(lib.util.match/replace a-query (match/replace a-query
[:field 40 opts] [:field 40 opts]
[:field 100 opts]))))) [:field 100 opts])))))
@@ -231,77 +203,53 @@
(t/testing "can we use `replace` to fix (legacy) `fk->` clauses where both args are unwrapped IDs?" (t/testing "can we use `replace` to fix (legacy) `fk->` clauses where both args are unwrapped IDs?"
(t/is (= {:query {:fields [[:fk-> [:field 1 nil] [:field 2 nil]] (t/is (= {:query {:fields [[:fk-> [:field 1 nil] [:field 2 nil]]
[:fk-> [:field 3 nil] [:field 4 nil]]]}} [:fk-> [:field 3 nil] [:field 4 nil]]]}}
(lib.util.match/replace-in (match/replace-in
{:query {:fields [[:fk-> 1 2] {:query {:fields [[:fk-> 1 2]
[:fk-> [:field 3 nil] [:field 4 nil]]]}} [:fk-> [:field 3 nil] [:field 4 nil]]]}}
[:query :fields] [:query :fields]
[:fk-> (source :guard integer?) (dest :guard integer?)] [:fk-> (source :guard integer?) (dest :guard integer?)]
[:fk-> [:field source nil] [:field dest nil]]))))) [:fk-> [:field source nil] [:field dest nil]])))))
(t/deftest ^:parallel replace-raw-keyword-patterns-test
(t/testing "does `replace` accept a raw keyword as the pattern the way `match` does?"
(t/is (= {:fields ["WOW" "WOW" "WOW"]}
(lib.util.match/replace another-query :field "WOW")))))
(t/deftest ^:parallel replace-set-of-keywords-test
(t/testing "does `replace` accept a set of keywords the way `match` does?"
(t/is (= {:fields ["WOW" "WOW" "WOW"]}
(lib.util.match/replace another-query #{:field :field-id} "WOW")))))
(t/deftest ^:parallel replace-&match-test (t/deftest ^:parallel replace-&match-test
(t/testing "can we use the anaphor `&match` to look at the entire match?" (t/testing "can we use the anaphor `&match` to look at the entire match?"
(t/is (= {:fields [[:field 1 nil] (t/is (= {:fields [[:field 1 nil]
[:magical-field [:field 2 {:temporal-unit :day}]] [:magical-field [:field 2 {:temporal-unit :day}]]
[:magical-field [:field 4 {:source-field 3, :temporal-unit :month}]]]} [:magical-field [:field 4 {:source-field 3, :temporal-unit :month}]]]}
(lib.util.match/replace another-query [:field _ (_ :guard :temporal-unit)] [:magical-field &match]))))) (match/replace another-query [:field _ (_ :guard :temporal-unit)] [:magical-field &match])))))
(t/deftest ^:parallel replace-&parents-test (t/deftest ^:parallel replace-&parents-test
(t/testing "can we use the anaphor `&parents` to look at the parents of the match?" (t/testing "can we use the anaphor `&parents` to look at the parents of the match?"
(t/is (= {:fields [[:a "WOW"] (t/is (= {:fields [[:a "WOW"]
[:b 200]]} [:b 200]]}
;; replace field ID clauses that are inside a datetime-field clause ;; replace field ID clauses that are inside a datetime-field clause
(lib.util.match/replace {:fields [[:a [:b 100]] (match/replace {:fields [[:a [:b 100]]
[:b 200]]} [:b 200]]}
:b [:b & _]
(if (contains? (set &parents) :a) (if (contains? (set &parents) :a)
"WOW" "WOW"
&match)))))) &match))))))
#?(:clj
(t/deftest ^:parallel replace-by-class-test
(t/testing "can we replace using a CLASS?"
(t/is (= [[:field 1 nil]
[:field 2 nil]
[:timestamp #inst "2018-10-08T00:00:00.000-00:00"]
4000]
(lib.util.match/replace [[:field 1 nil]
[:field 2 nil]
#inst "2018-10-08"
4000]
java.util.Date
[:timestamp &match]))))))
(t/deftest ^:parallel replace-by-predicate-test (t/deftest ^:parallel replace-by-predicate-test
(t/testing "can we replace using a PREDICATE?" (t/testing "can we replace using a PREDICATE?"
(t/is (= {:filter [:and (t/is (= {:filter [:and
[:= [:field nil nil] 4000.0] [:= [:field nil nil] 4000.0]
[:= [:field nil nil] 5000.0]]} [:= [:field nil nil] 5000.0]]}
;; find the integer args to `:=` clauses that are not inside `:field-id` clauses and make them FLOATS ;; find the integer args to `:=` clauses that are not inside `:field-id` clauses and make them FLOATS
(lib.util.match/replace {:filter [:and (match/replace {:filter [:and
[:= [:field 1 nil] 4000] [:= [:field 1 nil] 4000]
[:= [:field 2 nil] 5000]]} [:= [:field 2 nil] 5000]]}
integer? (_ :guard integer?)
(when (= := (last &parents)) (when (= := (last &parents))
(float &match))))))) (float &match)))))))
(t/deftest ^:parallel complex-replace-test (t/deftest ^:parallel complex-replace-test
(t/testing "can we do fancy stuff like remove all the filters that use datetime fields from a query?" (t/testing "can we do fancy stuff like remove all the filters that use datetime fields from a query?"
(t/is (= [:and nil [:= [:field 100 nil] 20]] (t/is (= [:and nil [:= [:field 100 nil] 20]]
(lib.util.match/replace [:and (match/replace [:and
[:= [:=
[:field 1 {:temporal-unit :day}] [:field 1 {:temporal-unit :day}]
[:absolute-datetime #inst "2016-11-08T00:00:00.000-00:00" :day]] [:absolute-datetime #inst "2016-11-08T00:00:00.000-00:00" :day]]
[:= [:field 100 nil] 20]] [:= [:field 100 nil] 20]]
[_ [:field _ (_ :guard :temporal-unit)] & _] nil))))) [_ [:field _ (_ :guard :temporal-unit)] & _] nil)))))
(t/deftest ^:parallel replace-short-circut-test (t/deftest ^:parallel replace-short-circut-test
@@ -311,9 +259,9 @@
[:field 20 {:temporal-unit :month}] [:field 20 {:temporal-unit :month}]
[:field 30 nil]] [:field 30 nil]]
(let [id-is-datetime-field? #{10}] (let [id-is-datetime-field? #{10}]
(lib.util.match/replace [[:field 10 nil] (match/replace [[:field 10 nil]
[:field 20 {:temporal-unit :month}] [:field 20 {:temporal-unit :month}]
[:field 30 nil]] [:field 30 nil]]
;; don't replace anything that's already wrapping a `field-id` ;; don't replace anything that's already wrapping a `field-id`
[_ [:field-id & _] & _] [_ [:field-id & _] & _]
&match &match
@@ -321,166 +269,166 @@
[:field (id :guard id-is-datetime-field?) opts] [:field (id :guard id-is-datetime-field?) opts]
[:field id (assoc opts :temporal-unit :day)])))))) [:field id (assoc opts :temporal-unit :day)]))))))
(t/deftest ^:parallel match-lite-test (t/deftest ^:parallel match-one-test
(t/is (= 6 (lib.util.match/match-lite [1 2 3] (t/is (= 6 (match/match-one [1 2 3]
[a b c] (+ a b c)))) [a b c] (+ a b c))))
(t/is (= 5 (lib.util.match/match-lite [1 :value1 4] (t/is (= 5 (match/match-one [1 :value1 4]
[var1 :value1 var2] (+ var1 var2)))) [var1 :value1 var2] (+ var1 var2))))
(t/is (= [1 2 [3 4 5]] (lib.util.match/match-lite [1 2 3 4 5] (t/is (= [1 2 [3 4 5]] (match/match-one [1 2 3 4 5]
[a b & rest] [a b rest]))) [a b & rest] [a b rest])))
(t/is (= 48 (lib.util.match/match-lite [2 4 6] (t/is (= 48 (match/match-one [2 4 6]
[a (b :guard even?) c] (* a b c)))) [a (b :guard even?) c] (* a b c))))
(t/is (= "matched odd" (t/is (= "matched odd"
(lib.util.match/match-lite [1 3 5] (match/match-one [1 3 5]
[a (b :guard even?) c] "matched even" [a (b :guard even?) c] "matched even"
[a b c] "matched odd"))) [a b c] "matched odd")))
(t/is (= "fallback: not a vector" (t/is (= "fallback: not a vector"
(lib.util.match/match-lite "not a vector" (match/match-one "not a vector"
x (str "fallback: " x)))) x (str "fallback: " x))))
;; Rest with guard ;; Rest with guard
(t/is (= "a=1 b=2 rest=[3 4 5]" (t/is (= "a=1 b=2 rest=[3 4 5]"
(lib.util.match/match-lite [1 2 3 4 5] (match/match-one [1 2 3 4 5]
[a b & (rst :guard (> (count rst) 2))] (str "a=" a " b=" b " rest=" rst)))) [a b & (rst :guard (> (count rst) 2))] (str "a=" a " b=" b " rest=" rst))))
(t/testing "Edge cases" (t/testing "Edge cases"
(t/testing "Empty collections" (t/testing "Empty collections"
(t/is (= :empty-vec (lib.util.match/match-lite [] (t/is (= :empty-vec (match/match-one []
[] :empty-vec))) [] :empty-vec)))
(t/is (= :empty-map (lib.util.match/match-lite {} (t/is (= :empty-map (match/match-one {}
{} :empty-map)))) {} :empty-map))))
(t/testing "Nil values" (t/testing "Nil values"
(t/is (= :nil-value (lib.util.match/match-lite nil (t/is (= :nil-value (match/match-one nil
nil :nil-value nil :nil-value
_ :not-nil)))) _ :not-nil))))
(t/testing "Boolean values" (t/testing "Boolean values"
(t/is (= :true-value (lib.util.match/match-lite true (t/is (= :true-value (match/match-one true
true :true-value true :true-value
false :false-value))) false :false-value)))
(t/is (= :false-value (lib.util.match/match-lite false (t/is (= :false-value (match/match-one false
true :true-value true :true-value
false :false-value)))))) false :false-value))))))
(t/deftest ^:parallel match-lite-or-syntax (t/deftest ^:parallel match-one-or-syntax
(t/is (= 10 (lib.util.match/match-lite [1 2 3] (t/is (= 10 (match/match-one [1 2 3]
(:or [a] [a b] [a b c]) (* a 10)))) (:or [a] [a b] [a b c]) (* a 10))))
(t/is (= nil (lib.util.match/match-lite [1 2 3] (t/is (= nil (match/match-one [1 2 3]
(:or [(a :guard even?) b] [a (b :guard odd?)]) (* a b)))) (:or [(a :guard even?) b] [a (b :guard odd?)]) (* a b))))
(t/testing ":or can mix with other patterns" (t/testing ":or can mix with other patterns"
(t/is (= 20 (lib.util.match/match-lite [1 2 3] (t/is (= 20 (match/match-one [1 2 3]
[a b c d] 10 [a b c d] 10
(:or [a b] [a b c]) 20 (:or [a b] [a b c]) 20
_ 30)))) _ 30))))
(t/testing "doesn't break if same symbols are used for different bindings" (t/testing "doesn't break if same symbols are used for different bindings"
(t/is (= 1 (lib.util.match/match-lite [1 2] (t/is (= 1 (match/match-one [1 2]
(:or [(a :guard even?) b] (:or [(a :guard even?) b]
[b a]) [b a])
b))))) b)))))
(t/deftest ^:parallel match-lite-and-syntax (t/deftest ^:parallel match-one-and-syntax
(t/is (= 10 (lib.util.match/match-lite [1 2 3] (t/is (= 10 (match/match-one [1 2 3]
(:and [a b c] (_ :guard vector?)) (* a 10)))) (:and [a b c] (_ :guard vector?)) (* a 10))))
(t/is (= nil (lib.util.match/match-lite [1 2 3] (t/is (= nil (match/match-one [1 2 3]
(:and [a b c] (_ :guard map?)) (* a 10)))) (:and [a b c] (_ :guard map?)) (* a 10))))
(t/is (= nil (lib.util.match/match-lite [1 2 3] (t/is (= nil (match/match-one [1 2 3]
(:and [a b] (_ :guard vector?)) (* a 10)))) (:and [a b] (_ :guard vector?)) (* a 10))))
(t/is (= 6 (lib.util.match/match-lite [1 2 3] (t/is (= 6 (match/match-one [1 2 3]
(:and [a b c] (v :guard vector?)) (reduce * 1 v)))) (:and [a b c] (v :guard vector?)) (reduce * 1 v))))
(t/testing "later patterns can refer to earlier bindings" (t/testing "later patterns can refer to earlier bindings"
(t/is (= 20 (lib.util.match/match-lite [1 2 3] (t/is (= 20 (match/match-one [1 2 3]
(:and [a b c] (:and [a b c]
(_ :guard (and (odd? a) (even? b)))) (_ :guard (and (odd? a) (even? b))))
(* a b 10)))))) (* a b 10))))))
(t/deftest ^:parallel match-lite-map-syntax (t/deftest ^:parallel match-one-map-syntax
(t/is (= 200 (lib.util.match/match-lite {:a 10, :b 20} (t/is (= 200 (match/match-one {:a 10, :b 20}
{:a a, :b b} (* a b)))) {:a a, :b b} (* a b))))
(t/is (= nil (lib.util.match/match-lite {:a 10} (t/is (= nil (match/match-one {:a 10}
{:a a, :b b} (* a b)))) {:a a, :b b} (* a b))))
(t/testing "&truthy" (t/testing "&truthy"
(t/is (= 123 (lib.util.match/match-lite {:a 10} (t/is (= 123 (match/match-one {:a 10}
{:a &truthy} 123))) {:a &truthy} 123)))
(t/is (= nil (lib.util.match/match-lite {:b 20} (t/is (= nil (match/match-one {:b 20}
{:a &truthy} 123))) {:a &truthy} 123)))
(t/is (= 123 (lib.util.match/match-lite {:a true} (t/is (= 123 (match/match-one {:a true}
{:a &truthy} 123))) {:a &truthy} 123)))
(t/is (= nil (lib.util.match/match-lite {:a false} (t/is (= nil (match/match-one {:a false}
{:a &truthy} 123))) {:a &truthy} 123)))
(t/is (= nil (lib.util.match/match-lite {:a nil} (t/is (= nil (match/match-one {:a nil}
{:a &truthy} 123)))) {:a &truthy} 123))))
(t/testing "_" (t/testing "_"
(t/is (= 123 (lib.util.match/match-lite {:a 10} (t/is (= 123 (match/match-one {:a 10}
{:a _} 123))) {:a _} 123)))
(t/is (= nil (lib.util.match/match-lite {:b 20} (t/is (= nil (match/match-one {:b 20}
{:a _} 123))) {:a _} 123)))
(t/is (= 123 (lib.util.match/match-lite {:a true} (t/is (= 123 (match/match-one {:a true}
{:a _} 123))) {:a _} 123)))
(t/is (= 123 (lib.util.match/match-lite {:a false} (t/is (= 123 (match/match-one {:a false}
{:a _} 123))))) {:a _} 123)))))
(t/deftest ^:parallel same-result-with-different-bindings-test (t/deftest ^:parallel same-result-with-different-bindings-test
(t/testing "result here should not be treated as a common because it refers to different bindings in branches" (t/testing "result here should not be treated as a common because it refers to different bindings in branches"
(t/is (= 1 (lib.util.match/match-lite [1 2] (t/is (= 1 (match/match-one [1 2]
[(a :guard (odd? a)) (b :guard (even? b))] (- b a) [(a :guard (odd? a)) (b :guard (even? b))] (- b a)
[(b :guard (even? b)) (a :guard (odd? a))] (- b a)))))) [(b :guard (even? b)) (a :guard (odd? a))] (- b a))))))
(t/deftest ^:parallel guard-predicate-test (t/deftest ^:parallel guard-predicate-test
(t/is (= -2 (lib.util.match/match-lite [2] (t/is (= -2 (match/match-one [2]
[(a :guard odd?)] a [(a :guard odd?)] a
[(b :guard even?)] (- b)))) [(b :guard even?)] (- b))))
(t/is (= -2 (lib.util.match/match-lite [2] (t/is (= -2 (match/match-one [2]
[(a :guard (odd? a))] a [(a :guard (odd? a))] a
[(b :guard (even? b))] (- b)))) [(b :guard (even? b))] (- b))))
(t/is (= -2 (lib.util.match/match-lite [2] (t/is (= -2 (match/match-one [2]
[(_ :guard odd?)] 1 [(_ :guard odd?)] 1
[(_ :guard even?)] -2))) [(_ :guard even?)] -2)))
(t/is (= 2 (lib.util.match/match-lite [{:b 2}] (t/is (= 2 (match/match-one [{:b 2}]
[(_ :guard :a)] 1 [(_ :guard :a)] 1
[(_ :guard :b)] 2))) [(_ :guard :b)] 2)))
(t/is (= :ok (lib.util.match/match-lite [3] (t/is (= :ok (match/match-one [3]
[(_ :guard #{1 2 3})] :ok))) [(_ :guard #{1 2 3})] :ok)))
#?(:clj (t/is (thrown? clojure.lang.Compiler$CompilerException #?(:clj (t/is (thrown? clojure.lang.Compiler$CompilerException
(eval '(lib.util.match/match-lite [1] (eval '(match/match-one [1]
[(a :guard #(odd? %))] a))))) [(a :guard #(odd? %))] a)))))
#?(:clj (t/is (thrown? clojure.lang.Compiler$CompilerException #?(:clj (t/is (thrown? clojure.lang.Compiler$CompilerException
(eval '(lib.util.match/match-lite [1] (eval '(match/match-one [1]
[(a :guard (fn [x] (odd? x)))] a)))))) [(a :guard (fn [x] (odd? x)))] a))))))
(t/deftest ^:parallel return-nil-matches-test (t/deftest ^:parallel return-nil-matches-test
(t/testing "a clause can return nil and that is still considered a match" (t/testing "a clause can return nil and that is still considered a match"
(t/is (= nil (lib.util.match/match-lite [:a nil] (t/is (= nil (match/match-one [:a nil]
[:a x] x [:a x] x
_ :default))))) _ :default)))))
(t/deftest ^:parallel recur-test (t/deftest ^:parallel recur-test
(t/testing "clause can choose to &recur the match with a different value" (t/testing "clause can choose to &recur the match with a different value"
(t/is (= :inside (lib.util.match/match-lite [[[[[[:inside]]]]]] (t/is (= :inside (match/match-one [[[[[[:inside]]]]]]
[in] (&recur in) [in] (&recur in)
_ &match))))) _ &match)))))
(t/deftest ^:parallel match-many-test (t/deftest ^:parallel match-many-test
(t/is (= [6 15] (lib.util.match/match-many [[1 2 3] [4 5 6]] (t/is (= [6 15] (match/match-many [[1 2 3] [4 5 6]]
[a b c] (+ a b c)))) [a b c] (+ a b c))))
(t/is (= [1 2 3 4 5 6] (lib.util.match/match-many [[1 2 3] [4 5 6]] (t/is (= [1 2 3 4 5 6] (match/match-many [[1 2 3] [4 5 6]]
(_ :guard number?) &match))) (_ :guard number?) &match)))
(t/is (= [100] (lib.util.match/match-many [[1 2 3] [4 5 6]] (t/is (= [100] (match/match-many [[1 2 3] [4 5 6]]
(_ :guard keyword?) &match (_ :guard keyword?) &match
_ 100))) _ 100)))
(t/testing "absent of matches returns nil" (t/testing "absent of matches returns nil"
(t/is (= nil (lib.util.match/match-many [[1 2 3] [4 5 6]] (t/is (= nil (match/match-many [[1 2 3] [4 5 6]]
(_ :guard keyword?) &match)))) (_ :guard keyword?) &match))))
(t/testing "nils aren't recorded into the result" (t/testing "nils aren't recorded into the result"
(t/is (= [15] (lib.util.match/match-many [[1 2 3] [4 5 6]] (t/is (= [15] (match/match-many [[1 2 3] [4 5 6]]
[a b c] (when (> a 1) [a b c] (when (> a 1)
(+ a b c))))))) (+ a b c)))))))
(t/deftest ^:parallel parents-tests (t/deftest ^:parallel parents-tests
(t/is (= [:foo :bar :baz :qux] (t/is (= [:foo :bar :baz :qux]
(lib.util.match/match-lite [:foo [:bar [:baz {:qux 42}]]] (match/match-one [:foo [:bar [:baz {:qux 42}]]]
42 &parents))) 42 &parents)))
(t/is (= [[:foo :bar :baz :qux] (t/is (= [[:foo :bar :baz :qux]
[:foo :bar :baz :corge]] [:foo :bar :baz :corge]]
(lib.util.match/match-many [:foo [:bar [:baz {:qux 42 :corge 42}]]] (match/match-many [:foo [:bar [:baz {:qux 42 :corge 42}]]]
42 &parents)))) 42 &parents))))

View File

@@ -5,11 +5,11 @@
[metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata :as lib.metadata]
[metabase.lib.test-metadata :as meta] [metabase.lib.test-metadata :as meta]
[metabase.lib.test-util :as lib.tu] [metabase.lib.test-util :as lib.tu]
[metabase.lib.util.match :as lib.util.match] [metabase.util.match :as match]
[metabase.warehouse-schema.metadata-queries :as metadata-queries])) [metabase.warehouse-schema.metadata-queries :as metadata-queries]))
(defn- sort-by-field-id [clauses] (defn- sort-by-field-id [clauses]
(sort-by #(lib.util.match/match-lite % (sort-by #(match/match-one %
[:field _opts (id :guard pos-int?)] [:field _opts (id :guard pos-int?)]
id) id)
clauses)) clauses))

View File

@@ -10,7 +10,6 @@
[metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata :as lib.metadata]
[metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.id :as lib.schema.id]
[metabase.lib.test-metadata :as meta] [metabase.lib.test-metadata :as meta]
[metabase.lib.util.match :as lib.util.match]
[metabase.models.interface :as mi] [metabase.models.interface :as mi]
[metabase.permissions.models.permissions :as perms] [metabase.permissions.models.permissions :as perms]
[metabase.permissions.models.permissions-group :as perms-group] [metabase.permissions.models.permissions-group :as perms-group]
@@ -22,6 +21,7 @@
[metabase.util :as u] [metabase.util :as u]
[metabase.util.malli :as mu] [metabase.util.malli :as mu]
[metabase.util.malli.registry :as mr] [metabase.util.malli.registry :as mr]
[metabase.util.match :as match]
[metabase.xrays.api.automagic-dashboards :as api.automagic-dashboards] [metabase.xrays.api.automagic-dashboards :as api.automagic-dashboards]
[metabase.xrays.automagic-dashboards.comparison :as comparison] [metabase.xrays.automagic-dashboards.comparison :as comparison]
[metabase.xrays.automagic-dashboards.core :as magic] [metabase.xrays.automagic-dashboards.core :as magic]
@@ -623,7 +623,7 @@
:let [query (get-in dashcard [:card :dataset_query])] :let [query (get-in dashcard [:card :dataset_query])]
:when query :when query
:let [breakouts (lib/breakouts query)] :let [breakouts (lib/breakouts query)]
id (lib.util.match/match-many breakouts id (match/match-many breakouts
[:field {:binning &truthy} (id :guard pos-int?)] [:field {:binning &truthy} (id :guard pos-int?)]
id)] id)]
id))))))))))) id)))))))))))
@@ -657,7 +657,7 @@
:let [query (get-in dashcard [:card :dataset_query])] :let [query (get-in dashcard [:card :dataset_query])]
:when query :when query
:let [breakouts (lib/breakouts query)] :let [breakouts (lib/breakouts query)]
id (lib.util.match/match-many breakouts id (match/match-many breakouts
[:field {:temporal-unit &truthy} (id :guard pos-int?)] [:field {:temporal-unit &truthy} (id :guard pos-int?)]
id)] id)]
id)] id)]

View File

@@ -1,11 +1,3 @@
(disable-warning
{:linter :suspicious-expression
:for-macro 'clojure.core/and
:if-inside-macroexpansion-of #{'clojure.core.match/match}
:within-depth 50
:reason (str "By default, eastwood only allows a depth of up to 13 when ignoring single-arg `and` "
"in `core/match` macro expansions; some of our `lib.util/match` macros exceed that depth.")})
(disable-warning (disable-warning
{:linter :wrong-arity {:linter :wrong-arity
:function-symbol 'metabase.test.data.users/user-http-request :function-symbol 'metabase.test.data.users/user-http-request