[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:
committed by
GitHub
parent
6404c3c029
commit
f9ce3f291d
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
15
.cljfmt.edn
15
.cljfmt.edn
@@ -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]]
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
5
deps.edn
5
deps.edn
@@ -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
|
||||||
|
|||||||
@@ -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) #"^\$" "")))
|
||||||
|
|
||||||
|
|||||||
@@ -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?
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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))))
|
||||||
|
|
||||||
|
|||||||
@@ -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))))
|
||||||
|
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)))
|
||||||
|
|||||||
@@ -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))]
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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])
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)])
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)]))))
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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))))
|
||||||
|
|||||||
@@ -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)])
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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))))))
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)))))
|
||||||
|
|||||||
@@ -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)))
|
||||||
|
|
||||||
|
|||||||
@@ -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))))
|
||||||
|
|||||||
@@ -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/*))))))
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -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))
|
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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))))
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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))))
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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?)]
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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))))
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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))]
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
7
src/metabase/util/match.cljs
Normal file
7
src/metabase/util/match.cljs
Normal 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)
|
||||||
84
src/metabase/util/match/impl.cljc
Normal file
84
src/metabase/util/match/impl.cljc
Normal 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))
|
||||||
@@ -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?"
|
||||||
|
|||||||
@@ -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?"
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)"
|
||||||
|
|||||||
@@ -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)])
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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))))
|
||||||
@@ -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))
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user