Enable codespell GitHub action for src folder (#68213)
This commit is contained in:
@@ -4,6 +4,8 @@ altho
|
||||
Brite
|
||||
cancelation
|
||||
cancelations
|
||||
caracteres
|
||||
copyin
|
||||
datas
|
||||
edn
|
||||
eduction
|
||||
@@ -13,6 +15,7 @@ horizontalX
|
||||
horizontalY
|
||||
juxt
|
||||
methodd
|
||||
nd
|
||||
notIn
|
||||
optionA
|
||||
ot
|
||||
@@ -29,3 +32,4 @@ trsn
|
||||
tru
|
||||
trun
|
||||
unparseable
|
||||
withs
|
||||
|
||||
@@ -19,4 +19,6 @@ skip =
|
||||
frontend/src/metabase/visualizations/lib/mapping_codes.ts,
|
||||
frontend/*/mocks/*,
|
||||
modules/drivers/mongo/src/metabase/driver/mongo/operators.clj,
|
||||
src/metabase/analyze/classifiers/name.clj,
|
||||
src/metabase/util/currency.cljc,
|
||||
ignore-words = .codespellignore
|
||||
|
||||
1
.github/workflows/codespell.yml
vendored
1
.github/workflows/codespell.yml
vendored
@@ -20,3 +20,4 @@ jobs:
|
||||
enterprise
|
||||
frontend
|
||||
modules/drivers
|
||||
src
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"enterprise"
|
||||
"frontend"
|
||||
"modules/drivers"
|
||||
#_"src"
|
||||
"src"
|
||||
#_"test"])
|
||||
|
||||
(defn codespell [_cli-args]
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
(defn apply-json-query
|
||||
"Executes a jq query on [[object]]."
|
||||
[object jq-query]
|
||||
;; TODO this is pretty ineficient. We parse with `:as :json`, then reencode within a response
|
||||
;; TODO this is pretty inefficient. We parse with `:as :json`, then re-encode within a response
|
||||
;; I couldn't find a way to get JSONNode out of cheshire, so we fall back to jackson.
|
||||
;; Should jackson be added explicitly to deps.edn?
|
||||
(let [json-node (.readTree ^ObjectMapper @object-mapper (json/encode object))
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
|
||||
;; NOTE: the query implementation `views-and-runs` has an order-by clause using most recent timestamp
|
||||
;; this has an effect on the outcomes. Consider an item with a massively high viewcount but a last view by the user
|
||||
;; a long time ago. This may not even make it into the firs 10 items from the query, even though it might be worth showing
|
||||
;; a long time ago. This may not even make it into the first 10 items from the query, even though it might be worth showing
|
||||
(when-not (zero? max-count)
|
||||
(* (/ cnt max-count) views-wt))])]
|
||||
(assoc item :score (double (reduce + scores))))) items))))
|
||||
|
||||
@@ -604,7 +604,7 @@
|
||||
|
||||
Returns: [:map [:recents [:sequential Item]]]
|
||||
|
||||
[[do-query]] can return nils, and we remove them here becuase there can be recent views for deleted entities, and we
|
||||
[[do-query]] can return nils, and we remove them here because there can be recent views for deleted entities, and we
|
||||
don't want to show those in the recent views.
|
||||
|
||||
Options:
|
||||
|
||||
@@ -208,13 +208,13 @@
|
||||
(prometheus/gauge :jetty/dispatched-active
|
||||
{:description "Number of active requests being handled"})
|
||||
(prometheus/gauge :jetty/dispatched-active-max
|
||||
{:descrption "Maximum number of active requests handled"})
|
||||
{:description "Maximum number of active requests handled"})
|
||||
(prometheus/gauge :jetty/dispatched-time-max
|
||||
{:description "Maximum time spent dispatching a request"})
|
||||
(prometheus/counter :jetty/dispatched-time-seconds-total
|
||||
{:descrption "Total time spent handling requests"})
|
||||
{:description "Total time spent handling requests"})
|
||||
(prometheus/counter :jetty/async-requests-total
|
||||
{:descrption "Totql number of async requests"})
|
||||
{:description "Total number of async requests"})
|
||||
(prometheus/gauge :jetty/async-requests-waiting
|
||||
{:description "Currently waiting async requests"})
|
||||
(prometheus/gauge :jetty/async-requests-waiting-max
|
||||
@@ -222,9 +222,9 @@
|
||||
(prometheus/counter :jetty/async-dispatches-total
|
||||
{:description "Number of requests that have been asynchronously dispatched"})
|
||||
(prometheus/counter :jetty/expires-total
|
||||
{:descpription "Number of async requests that have expired"})
|
||||
{:description "Number of async requests that have expired"})
|
||||
(prometheus/counter :jetty/responses-total
|
||||
{:descrption "Total response grouped by status code"
|
||||
{:description "Total response grouped by status code"
|
||||
:labels [:code]})
|
||||
(prometheus/counter :jetty/responses-bytes-total
|
||||
{:description "Total number of bytes across all responses"})])
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
;; +----------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
(defn create-job-execution-listener
|
||||
"Creates an instance of an anonymous JobListener to record exeuction metrics to prometheus."
|
||||
"Creates an instance of an anonymous JobListener to record execution metrics to prometheus."
|
||||
^JobListener [] ; No registry argument needed
|
||||
(reify JobListener
|
||||
(getName [_]
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
::analyze.schema/qp-results-cased-map])
|
||||
|
||||
(mu/defn infer-semantic-type-by-name :- [:maybe :keyword]
|
||||
"Classifer that infers the semantic type of a `field` based on its name and base type."
|
||||
"Classifier that infers the semantic type of a `field` based on its name and base type."
|
||||
[field-or-column :- FieldOrColumn]
|
||||
;; Don't overwrite keys, else we're ok with overwriting as a new more precise type might have
|
||||
;; been added.
|
||||
@@ -182,7 +182,7 @@
|
||||
[(prefix-or-postfix "vendor") :entity/CompanyTable]])
|
||||
|
||||
(mu/defn infer-entity-type-by-name :- analyze.schema/Table
|
||||
"Classifer that infers the semantic type of a `table` based on its name."
|
||||
"Classifier that infers the semantic type of a `table` based on its name."
|
||||
[table :- analyze.schema/Table]
|
||||
(let [table-name (-> table :name u/lower-case-en)]
|
||||
(assoc table :entity_type (or (some (fn [[pattern type]]
|
||||
|
||||
@@ -253,7 +253,7 @@
|
||||
Boolean (->number [this] (if this 1 0))
|
||||
Number (->number [this] this)
|
||||
String (->number [this]
|
||||
;; faster to be optimistic and fail than to explicitely test and dispatch
|
||||
;; faster to be optimistic and fail than to explicitly test and dispatch
|
||||
(or (parse-long this)
|
||||
(parse-double this))))
|
||||
|
||||
|
||||
@@ -289,7 +289,7 @@
|
||||
(sync-util/name-for-logging (mi/instance :model/Field datetime))))))
|
||||
|
||||
(defn insights
|
||||
"Based on the shape of returned data construct a transducer to statistically analyize data."
|
||||
"Based on the shape of returned data construct a transducer to statistically analyze data."
|
||||
[cols]
|
||||
(let [cols-by-type (->> cols
|
||||
(map-indexed (fn [idx col]
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
col
|
||||
:semantic_type
|
||||
(fn [original-value]
|
||||
;; If we already know the semantic type, becouse it is stored, don't classify again, but try to refine semantic
|
||||
;; If we already know the semantic type, because it is stored, don't classify again, but try to refine semantic
|
||||
;; type set upstream for aggregation cols (which come back as :type/Number).
|
||||
(case original-value
|
||||
(nil :type/Number) (classifiers.name/infer-semantic-type-by-name col)
|
||||
|
||||
@@ -36,13 +36,13 @@
|
||||
;;
|
||||
;; <hr />
|
||||
;;
|
||||
;; ## How does defendpoint coersion work?
|
||||
;; ## How does defendpoint coercion work?
|
||||
;;
|
||||
;; The `defendpoint` macro uses the schemas to generate code which binds args to their decoded values. Values are
|
||||
;; decoded by their corresponding malli schema. n.b.: Only symbols in the arg->schema map will be coerced; unmentioned
|
||||
;; aliases will not be bound.
|
||||
;;
|
||||
;; The exact coersion function [[mc/decode]], uses the [[metabase.api.macros/decode-transformer]],
|
||||
;; The exact coercion function [[mc/decode]], uses the [[metabase.api.macros/decode-transformer]],
|
||||
;; and gets called with the schema, value, and transformer. see: https://github.com/metosin/malli#value-transformation
|
||||
;; for more details
|
||||
;;
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
;;; having two routes with the same method and param that only differ by regex patterns. It makes using this stuff more
|
||||
;;; annoying
|
||||
(mr/def ::unique-key
|
||||
"Unique indentifier for an api endpoint. `(:api/endpoints (meta a-namespace))` is a map of `::unique-key` => `::info`"
|
||||
"Unique identifier for an api endpoint. `(:api/endpoints (meta a-namespace))` is a map of `::unique-key` => `::info`"
|
||||
[:tuple
|
||||
#_method ::method
|
||||
#_route string?
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
e)))))
|
||||
|
||||
(defn- mjs-collect-definitions
|
||||
"We transform json-schema in a few different places, but we need to collect all defitions in a single one."
|
||||
"We transform json-schema in a few different places, but we need to collect all definitions in a single one."
|
||||
[malli-schema]
|
||||
(let [jss (mjs/transform malli-schema {::mjs/definitions-path "#/components/schemas/"})]
|
||||
(when *definitions*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns metabase.api.util
|
||||
"Random utilty endpoints for things that don't belong anywhere else in particular, e.g. endpoints for certain admin
|
||||
"Random utility endpoints for things that don't belong anywhere else in particular, e.g. endpoints for certain admin
|
||||
page tasks."
|
||||
(:require
|
||||
[metabase.api.macros :as api.macros]
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
(throw e)))))))
|
||||
|
||||
(defmacro with-cluster-lock
|
||||
"Run `body` in a tranactions that tries to take a lock from the metabase_cluster_lock table of
|
||||
"Run `body` in a transaction that tries to take a lock from the metabase_cluster_lock table of
|
||||
the specified name to coordinate concurrency with other metabase instances sharing the appdb."
|
||||
([lock-options & body]
|
||||
`(do-with-cluster-lock ~lock-options (fn [] ~@body))))
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
"idleConnectionTestPeriod"
|
||||
60
|
||||
;;
|
||||
;; The fully qualified class-name of an implememtation of the ConnectionCustomizer interface, which users can
|
||||
;; The fully qualified class-name of an implementation of the ConnectionCustomizer interface, which users can
|
||||
;; implement to set up Connections when they are acquired from the database, or on check-out, and potentially to
|
||||
;; clean things up on check-in and Connection destruction. If standard Connection properties (holdability, readOnly,
|
||||
;; or transactionIsolation) are set in the ConnectionCustomizer's onAcquire() method, these will override the
|
||||
|
||||
@@ -914,7 +914,7 @@
|
||||
(when (not= (raw-setting :ldap-sync-admin-group) "true")
|
||||
(remove-admin-group-from-mappings-by-setting-key! :ldap-group-mappings))
|
||||
;; sso are enterprise feature but we still run this even in OSS in case a customer
|
||||
;; have switched from enterprise -> SSO and stil have this mapping in Setting table
|
||||
;; have switched from enterprise -> SSO and still have this mapping in Setting table
|
||||
(remove-admin-group-from-mappings-by-setting-key! :jwt-group-mappings)
|
||||
(remove-admin-group-from-mappings-by-setting-key! :saml-group-mappings))
|
||||
|
||||
@@ -1334,7 +1334,7 @@
|
||||
|
||||
;; when card display is area or bar,
|
||||
;; 1. set the display key to :stackable.stack_display value OR leave it the same
|
||||
;; 2. when series settings exist, remove the diplay key from each map in the series_settings list
|
||||
;; 2. when series settings exist, remove the display key from each map in the series_settings list
|
||||
|
||||
(defn- area-bar-stacked-viz-migration
|
||||
[{display :display viz :visualization_settings :as card}]
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
(env-defaults db-type)))
|
||||
|
||||
(def env
|
||||
"Metabase Datatbase environment. Used to setup *application-db* and audit-db for enterprise users."
|
||||
"Metabase Database environment. Used to setup *application-db* and audit-db for enterprise users."
|
||||
(env* (config/config-kw :mb-db-type)))
|
||||
|
||||
(def db-type
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
;; Since MySQL TIMESTAMPs aren't timezone-aware this means comparisons are done between timestamps in the report
|
||||
;; timezone and the local datetime portion of the parameter, in UTC. Bad!
|
||||
;;
|
||||
;; Convert it to a LocalDateTime, in the report timezone, so comparisions will work correctly.
|
||||
;; Convert it to a LocalDateTime, in the report timezone, so comparisons will work correctly.
|
||||
;;
|
||||
;; See also — https://dev.mysql.com/doc/refman/5.5/en/datetime.html
|
||||
(let [offset (.. (t/zone-id) getRules (getOffset (t/instant t)))
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
;;; https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html
|
||||
;;;
|
||||
;;; MySQL can return 1317 and 3024, but 1969 is not an error code in the mysql reference. All of these codes make sense
|
||||
;;; for MariaDB to return. Hibernate expects 3024, but in testing 1969 was observered.
|
||||
;;; for MariaDB to return. Hibernate expects 3024, but in testing 1969 was observed.
|
||||
(defmethod query-canceled-exception?* :mysql
|
||||
[_db-type ^java.sql.SQLException e]
|
||||
;; apparently there is no enum in the MariaDB JDBC driver for different error codes >:(
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
720)
|
||||
|
||||
(defn log-minimum-value-warning
|
||||
"Logs a warning that the value for `audit-max-retention-days` is below the allowed minimum and will be overriden."
|
||||
"Logs a warning that the value for `audit-max-retention-days` is below the allowed minimum and will be overridden."
|
||||
[env-var-value]
|
||||
(log/warnf "MB_AUDIT_MAX_RETENTION_DAYS is set to %d; using the minimum value of %d instead."
|
||||
env-var-value
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
["card" "dashboard" "collection" "document"]))
|
||||
|
||||
(def BookmarkOrderings
|
||||
"Schema for an ordered of boomark orderings"
|
||||
"Schema for an ordered of bookmark orderings"
|
||||
[:sequential [:map
|
||||
[:type Models]
|
||||
[:item_id ms/PositiveInt]]])
|
||||
|
||||
2
src/metabase/cache/settings.clj
vendored
2
src/metabase/cache/settings.clj
vendored
@@ -13,7 +13,7 @@
|
||||
|
||||
(def ^:private ^:const global-max-caching-kb
|
||||
"Although depending on the database, we can support much larger cached values (1GB for PG, 2GB for H2 and 4GB for
|
||||
MySQL) we are not curretly setup to deal with data of that size. The datatypes we are using will hold this data in
|
||||
MySQL) we are not currently setup to deal with data of that size. The datatypes we are using will hold this data in
|
||||
memory and will not truly be streaming. This is a global max in order to prevent our users from setting the caching
|
||||
value so high it becomes a performance issue. The value below represents 200MB"
|
||||
(* 200 1024))
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
true
|
||||
(catch Exception e
|
||||
(let [data (ex-data e)]
|
||||
;; throw an appriopriate error if it's a connection error
|
||||
;; throw an appropriate error if it's a connection error
|
||||
(if (= ::http/unexceptional-status (:type data))
|
||||
(throw (ex-info (tru "Failed to connect to channel") {:request-status (:status data)
|
||||
:request-body (maybe-parse-json (:body data))}))
|
||||
|
||||
@@ -517,7 +517,7 @@
|
||||
enabled? :enabled} funnel-viz
|
||||
:when enabled?]
|
||||
[k (get rows k)]))
|
||||
;; re-create the rows with the label/visibilty specified in the funnel-viz
|
||||
;; re-create the rows with the label/visibility specified in the funnel-viz
|
||||
(let [rows-data (map (fn [{k :key enabled? :enabled} [_ value]]
|
||||
(when enabled?
|
||||
[k value])) funnel-viz raw-rows)]
|
||||
|
||||
@@ -234,7 +234,7 @@
|
||||
(:content (render-pulse-card :inline timezone-id card nil results options))))
|
||||
|
||||
(mu/defn render-pulse-section :- ::body/RenderedPartCard
|
||||
"Render a single Card section of a Pulse to a Hiccup form (representating HTML)."
|
||||
"Render a single Card section of a Pulse to a Hiccup form (representing HTML)."
|
||||
([timezone-id part]
|
||||
(render-pulse-section timezone-id part {}))
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
(defn style-tag-nonce-middleware
|
||||
"Constructs a middleware handler function that adds the generated nonce to an html string.
|
||||
This is only designed to be used with an endpoint that returns an html string response containing
|
||||
a style tag with an attribute 'nonce=%NONCE%'. Specifcally, this was designed to be used with the
|
||||
a style tag with an attribute 'nonce=%NONCE%'. Specifically, this was designed to be used with the
|
||||
endpoint `api/pulse/preview_dashboard/:id`."
|
||||
[only-this-uri handler]
|
||||
(fn [request respond raise]
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"#4C5773")
|
||||
|
||||
(def ^:const color-border
|
||||
"Used as color for the border of table, table header, and table body rows for charts with `:table` vizualization."
|
||||
"Used as color for the border of table, table header, and table body rows for charts with `:table` visualization."
|
||||
"#F0F0F0")
|
||||
|
||||
;; don't try to improve the code and make this a plain variable, in EE it's customizable which is why it's a function.
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
(throw e)))))
|
||||
|
||||
(defn user-transform
|
||||
"Tranformation from slack api user to our own internal representation."
|
||||
"Transformation from slack api user to our own internal representation."
|
||||
[member]
|
||||
{:display-name (str \@ (:name member))
|
||||
:type "user"
|
||||
@@ -226,7 +226,7 @@
|
||||
(defn bug-report-channel
|
||||
"Looks in [[slack-cached-channels-and-usernames]] to check whether a channel exists with the expected name from the
|
||||
[[slack-bug-report-channel]] setting with an # prefix. If it does, returns the channel details as a map. If it doesn't,
|
||||
throws an error that advices an admin to create it."
|
||||
throws an error that advises an admin to create it."
|
||||
[]
|
||||
(let [channel-name (channel.settings/slack-bug-report-channel)]
|
||||
(if (channel-exists? channel-name)
|
||||
|
||||
@@ -185,7 +185,7 @@
|
||||
;; This endpoint, and only this one in this ns, needs a backwards and forward compatible
|
||||
;; key conversion. This can be removed when Harbormaster does only underscores in the API,
|
||||
;; and the keys above (multipart-upload-id multipart-urls) renamed to use underscores.
|
||||
;; But it can also stay here indefinitly and will be correct.
|
||||
;; But it can also stay here indefinitely and will be correct.
|
||||
u/deep-kebab-keys)
|
||||
|
||||
etags
|
||||
@@ -214,7 +214,7 @@
|
||||
Will exit early if migration has been cancelled in any cluster instance.
|
||||
Should run in a separate thread since it can take a long time to complete."
|
||||
[{:keys [id external_id] :as migration} & {:keys [retry?]}]
|
||||
;; dump-to-h2 starts behaving oddly if you try to dump repeatly to the same file
|
||||
;; dump-to-h2 starts behaving oddly if you try to dump repeatedly to the same file
|
||||
;; in the same process, so use a random name.
|
||||
;; The docker image process runs in non-root, so write to a dir it can access.
|
||||
(let [dump-file (io/file (System/getProperty "java.io.tmpdir")
|
||||
@@ -258,7 +258,7 @@
|
||||
(catch Exception e
|
||||
;; See set-progress for when :terminal is set.
|
||||
(if (-> e ex-data :terminal)
|
||||
(log/info "Migration interruped due to terminal state")
|
||||
(log/info "Migration interrupted due to terminal state")
|
||||
(do
|
||||
(t2/update! :model/CloudMigration id {:state :error})
|
||||
(log/info "Migration failed")
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
:model/MetabotMessage
|
||||
:model/MetabotPrompt])))
|
||||
|
||||
(defn- objects->colums+values
|
||||
(defn- objects->columns+values
|
||||
"Given a sequence of objects/rows fetched from the H2 DB, return a the `columns` that should be used in the `INSERT`
|
||||
statement, and a sequence of rows (as sequences)."
|
||||
[target-db-type objs]
|
||||
@@ -156,7 +156,7 @@
|
||||
[target-db-type target-db-conn-spec table-name chunkk]
|
||||
(log/debugf "Inserting chunk of %d rows" (count chunkk))
|
||||
(try
|
||||
(let [{:keys [cols vals]} (objects->colums+values target-db-type chunkk)]
|
||||
(let [{:keys [cols vals]} (objects->columns+values target-db-type chunkk)]
|
||||
(jdbc/insert-multi! target-db-conn-spec table-name cols vals {:transaction? false}))
|
||||
(catch SQLException e
|
||||
(log/error (with-out-str (jdbc/print-sql-exception-chain e)))
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
(str (apply str (take n (repeat "#"))) " `" s "`"))
|
||||
|
||||
(defn- format-description
|
||||
"Helper function to specify description format for enviromnent variable docs."
|
||||
"Helper function to specify description format for environment variable docs."
|
||||
[env-var]
|
||||
(->> ((:description env-var))
|
||||
u/add-period
|
||||
|
||||
@@ -466,7 +466,7 @@
|
||||
|
||||
(mu/defn format-personal-collection-name :- ms/NonBlankString
|
||||
"Constructs the personal collection name from user name.
|
||||
When displaying to users we'll tranlsate it to user's locale,
|
||||
When displaying to users we'll translate it to user's locale,
|
||||
but to keeps things consistent in the database, we'll store the name in site's locale.
|
||||
|
||||
Practically, use `user-or-site` = `:site` when insert or update the name in database,
|
||||
@@ -528,7 +528,7 @@
|
||||
|
||||
(def ^:private CollectionWithLocationAndPersonalOwnerID
|
||||
"Schema for a Collection instance that has a valid `:location`, and a `:personal_owner_id` key *present* (but not
|
||||
neccesarily non-nil)."
|
||||
necessarily non-nil)."
|
||||
[:map
|
||||
[:location LocationPath]
|
||||
[:personal_owner_id [:maybe ms/PositiveInt]]])
|
||||
@@ -587,7 +587,7 @@
|
||||
(def ^:private ^{:arglists '([user-id])} user->personal-collection-id
|
||||
"Cached function to fetch the ID of the Personal Collection belonging to User with `user-id`. Since a Personal
|
||||
Collection cannot be deleted, the ID will not change; thus it is safe to cache, saving a DB call. It is also
|
||||
required to caclulate the Current User's permissions set, which is done for every API call; thus it is cached to
|
||||
required to calculate the Current User's permissions set, which is done for every API call; thus it is cached to
|
||||
save a DB call for *every* API call."
|
||||
(memoize/ttl
|
||||
^{::memoize/args-fn (fn [[user-id]]
|
||||
@@ -968,7 +968,7 @@
|
||||
collections)))
|
||||
|
||||
;;; +----------------------------------------------------------------------------------------------------------------+
|
||||
;;; | Nested Collections: Ancestors, Childrens, Child Collections |
|
||||
;;; | Nested Collections: Ancestors, Children, Child Collections |
|
||||
;;; +----------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
(mu/defn- ancestors* :- [:maybe [:sequential (ms/InstanceOf :model/Collection)]]
|
||||
@@ -1882,7 +1882,7 @@
|
||||
(if (and (= (u/qualified-name (:namespace collection)) "snippets")
|
||||
(not (premium-features/enable-snippet-collections?)))
|
||||
#{}
|
||||
;; This is not entirely accurate as you need to be a superuser to modifiy a collection itself (e.g., changing its
|
||||
;; This is not entirely accurate as you need to be a superuser to modify a collection itself (e.g., changing its
|
||||
;; name) but if you have write perms you can add/remove cards
|
||||
#{(case read-or-write
|
||||
:read (perms/collection-read-path collection-or-id)
|
||||
|
||||
@@ -966,9 +966,9 @@
|
||||
[select-columns necessary-columns]
|
||||
(let [columns (m/index-by select-name select-columns)]
|
||||
(map (fn [col]
|
||||
(let [[col-name typpe] (u/one-or-many col)]
|
||||
(get columns col-name (if (and typpe (= (mdb/db-type) :postgres))
|
||||
[(h2x/cast typpe nil) col-name]
|
||||
(let [[col-name type'] (u/one-or-many col)]
|
||||
(get columns col-name (if (and type' (= (mdb/db-type) :postgres))
|
||||
[(h2x/cast type' nil) col-name]
|
||||
[nil col-name]))))
|
||||
necessary-columns)))
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@
|
||||
parse-long))
|
||||
|
||||
(defonce ^{:doc "This UUID is randomly-generated upon launch and used to identify this specific Metabase instance during
|
||||
this specifc run. Restarting the server will change this UUID, and each server in a horizontal cluster
|
||||
this specific run. Restarting the server will change this UUID, and each server in a horizontal cluster
|
||||
will have its own ID, making this different from the `site-uuid` Setting."}
|
||||
local-process-uuid
|
||||
(str (random-uuid)))
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
10)
|
||||
|
||||
(mu/defn delete-extra-reviews!
|
||||
"Delete extra reviews to maintain an invariant of only `max-moderation-reviews`. Called before inserting so actuall
|
||||
insures there are one fewer than that so you can add afterwards."
|
||||
"Delete extra reviews to maintain an invariant of only `max-moderation-reviews`. Called before inserting so actually
|
||||
ensures there are one fewer than that so you can add afterwards."
|
||||
[item-id :- :int
|
||||
item-type :- :string]
|
||||
(let [ids (into #{} (comp (map :id)
|
||||
@@ -55,7 +55,7 @@
|
||||
:where [:and
|
||||
[:= :moderated_item_id item-id]
|
||||
[:= :moderated_item_type item-type]]
|
||||
;; cannot put the offset in this query as mysql doesnt place nice. It requires a limit
|
||||
;; cannot put the offset in this query as mysql doesn't play nice. It requires a limit
|
||||
;; as well which we do not want to give. The offset is only 10 though so its not a huge
|
||||
;; savings and we run this on every entry so the max number is 10, delete the extra,
|
||||
;; and insert a new one to arrive at 10 again, our invariant.
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
(defn the-driver
|
||||
"Like [[clojure.core/the-ns]]. Converts argument to a keyword, then loads and registers the driver if not already done,
|
||||
throwing an Exception if it fails or is invalid. Returns keyword. Note that this does not neccessarily mean the
|
||||
throwing an Exception if it fails or is invalid. Returns keyword. Note that this does not necessarily mean the
|
||||
driver is initialized (e.g., its full implementation and deps might not be loaded into memory) -- see also
|
||||
[[the-initialized-driver]].
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
(the-initialized-driver driver))
|
||||
|
||||
;;; +----------------------------------------------------------------------------------------------------------------+
|
||||
;;; | Interface (Multimethod Defintions) |
|
||||
;;; | Interface (Multimethod Definitions) |
|
||||
;;; +----------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
;; Methods a driver can implement. Not all of these are required; some have default implementations immediately below
|
||||
@@ -164,7 +164,7 @@
|
||||
;; (driver/register-driver! :my-driver, :parent :sql-jdbc)
|
||||
;;
|
||||
;; (defmethod driver/describe-table :my-driver [driver database table]
|
||||
;; (-> ((get-method driver/describe-table :sql-jdbc) driver databse table)
|
||||
;; (-> ((get-method driver/describe-table :sql-jdbc) driver database table)
|
||||
;; (update :tables add-materialized-views)))
|
||||
;;
|
||||
;; Make sure to pass along the `driver` parameter-as when you call other methods, rather than hardcoding the name of
|
||||
@@ -181,8 +181,8 @@
|
||||
manifest file (which is supplied for lazy-loaded drivers). Methods that require connecting to a database dispatch
|
||||
off of [[the-initialized-driver]], which will initialize a driver if not already done so.
|
||||
|
||||
You will rarely need to write an implentation for this method yourself. A lazy-loaded driver (like most of the
|
||||
Metabase drivers in v1.0 and above) are automatiaclly given an implentation of this method that performs the
|
||||
You will rarely need to write an implementation for this method yourself. A lazy-loaded driver (like most of the
|
||||
Metabase drivers in v1.0 and above) are automatically given an implementation of this method that performs the
|
||||
`init-steps` specified in the plugin manifest (such as loading namespaces in question).
|
||||
|
||||
If you do need to implement this method yourself, you do not need to call parent implementations. We'll take care of
|
||||
@@ -203,7 +203,7 @@
|
||||
implementation capitializes the name of the driver, e.g. `:oracle` becomes \"Oracle\".
|
||||
|
||||
When writing a driver that you plan to ship as a separate, lazy-loading plugin (including core drivers packaged this
|
||||
way, like SQLite), you do not need to implement this method; instead, specifiy it in your plugin manifest, and
|
||||
way, like SQLite), you do not need to implement this method; instead, specify it in your plugin manifest, and
|
||||
`lazy-loaded-driver` will create an implementation for you. Probably best if we only have one place where we set
|
||||
values for this."
|
||||
{:added "0.32.0" :arglists '([driver])}
|
||||
@@ -279,7 +279,7 @@
|
||||
|
||||
(defmulti describe-database
|
||||
"Return a map containing information that describes all of the tables in a `database`, an instance of the `Database`
|
||||
model. It is expected that this function will be peformant and avoid draining meaningful resources of the database.
|
||||
model. It is expected that this function will be performant and avoid draining meaningful resources of the database.
|
||||
Results should match the [[metabase.sync.interface/DatabaseMetadata]] schema.
|
||||
Multimethod for backwards compatibility, but should not be extended directly, should instead implement [[describe-database*]].
|
||||
Default impl invokes [[describe-database*]] wrapped in [[do-with-resilient-connection]]"
|
||||
@@ -295,7 +295,7 @@
|
||||
(defmulti describe-table
|
||||
"Return a map containing a single field `:fields` that describes the fields in a `table`. `database` will be an
|
||||
instance of the `Database` model; and `table`, an instance of the `Table` model. It is expected that this function
|
||||
will be peformant and avoid draining meaningful resources of the database. The value of `:fields` should be a set of
|
||||
will be performant and avoid draining meaningful resources of the database. The value of `:fields` should be a set of
|
||||
values matching the [[metabase.sync.interface/TableMetadataField]] schema."
|
||||
{:added "0.32.0" :arglists '([driver database table])}
|
||||
dispatch-on-initialized-driver
|
||||
@@ -578,7 +578,7 @@
|
||||
:binning
|
||||
|
||||
;; Does this driver not let you specify whether or not our string search filter clauses (`:contains`,
|
||||
;; `:starts-with`, and `:ends-with`, collectively the equivalent of SQL `LIKE`) are case-senstive or not? This
|
||||
;; `:starts-with`, and `:ends-with`, collectively the equivalent of SQL `LIKE`) are case-sensitive or not? This
|
||||
;; informs whether we should present you with the 'Case Sensitive' checkbox in the UI. At the time of this writing
|
||||
;; SQLite, SQLServer, and MySQL do not support this -- `LIKE` clauses are always case-insensitive.
|
||||
;;
|
||||
@@ -933,14 +933,14 @@
|
||||
|
||||
At the time of writing, this method acts as identity for nosql drivers. However, story with sql drivers is a bit
|
||||
different. To extend it for sql drivers, developers could use [[metabase.driver.sql.util/format-sql]]. Function
|
||||
in question is implemented in a way, that developers, implemnting this multimethod can:
|
||||
in question is implemented in a way, that developers, implementing this multimethod can:
|
||||
- Avoid implementing it completely, if their driver keyword representation corresponds to key in
|
||||
[[metabase.driver.sql.util/dialects]] (eg. `:postgres`).
|
||||
- Ignore implementing it, if it is sufficient to format their drivers native form with dialect corresponding
|
||||
to `:standardsql`'s value from the dialects map (eg `:h2`).
|
||||
- Use [[metabase.driver.sql.util/format-sql]] in this method's implementation, providing dialect keyword
|
||||
representation that corresponds to to their driver's formatting (eg. `:sqlserver` uses `:tsql`).
|
||||
- Completly reimplement this method with their special formatting code."
|
||||
- Completely reimplement this method with their special formatting code."
|
||||
{:added "0.47.0", :arglists '([driver native-form])}
|
||||
dispatch-on-initialized-driver
|
||||
:hierarchy #'hierarchy)
|
||||
@@ -1017,7 +1017,7 @@
|
||||
is only used for iterating over the values in a `_metabase_metadata` table. As such, the results are not expected to
|
||||
be returned lazily. There is no expectation that the results be returned in any given order.
|
||||
|
||||
This method is currently only used by the H2 driver to load the Sample Database, so it is not neccesary for any other
|
||||
This method is currently only used by the H2 driver to load the Sample Database, so it is not necessary for any other
|
||||
drivers to implement it at this time."
|
||||
{:added "0.32.0" :arglists '([driver database table])}
|
||||
dispatch-on-initialized-driver
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
;;
|
||||
;; `value`" is either:
|
||||
;; * `no-value`
|
||||
;; * A map contianing the value and type info for the value, e.g.
|
||||
;; * A map containing the value and type info for the value, e.g.
|
||||
;;
|
||||
;; {:type :date/single
|
||||
;; :value #t "2019-09-20T19:52:00.000-07:00"}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
;; For parsing date strings and producing either a date range (for raw SQL parameter substitution) or a MBQL clause
|
||||
|
||||
;; Decorders consist of:
|
||||
;; Decoders consist of:
|
||||
;; 1) Parser which tries to parse the date parameter string
|
||||
;; 2) Range decoder which takes the parser output and produces a date range relative to the given datetime
|
||||
;; 3) Filter decoder which takes the parser output and produces a mbql clause for a given mbql field reference
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
(defn- db-identifier->name
|
||||
"Get the name of identifier from JDBC error message.
|
||||
An identifier can contains quote and full schema, database, table , etc.
|
||||
This formats so that we get only the identifer name with quote removed.
|
||||
This formats so that we get only the identifier name with quote removed.
|
||||
|
||||
|
||||
(db-identifier->name \"PUBLIC.TABLE\" ) => \"TABLE\""
|
||||
@@ -61,7 +61,7 @@
|
||||
constraint-name table-name)
|
||||
(reduced nil))))
|
||||
[[] nil nil]
|
||||
(jdbc/reducible-query jdbc-spec sql-args {:identifers identity, :transaction? false})))))
|
||||
(jdbc/reducible-query jdbc-spec sql-args {:transaction? false})))))
|
||||
|
||||
(defmethod sql-jdbc.actions/maybe-parse-sql-error [:h2 driver-api/violate-not-null-constraint]
|
||||
[_driver error-type _database _action-type error-message]
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
(throw (Exception. (tru "Could not load {0} driver." driver) e))))))
|
||||
|
||||
(mu/defn load-driver-namespace-if-needed!
|
||||
"Load the expected namespace for a `driver` if it has not already been registed. This only works for core Metabase
|
||||
"Load the expected namespace for a `driver` if it has not already been registered. This only works for core Metabase
|
||||
drivers, whose namespaces follow an expected pattern; drivers provided by 3rd-party plugins are expected to register
|
||||
themselves in their plugin initialization code.
|
||||
|
||||
|
||||
@@ -736,7 +736,7 @@
|
||||
;; Since MySQL TIMESTAMPs aren't timezone-aware this means comparisons are done between timestamps in the report
|
||||
;; timezone and the local datetime portion of the parameter, in UTC. Bad!
|
||||
;;
|
||||
;; Convert it to a LocalDateTime, in the report timezone, so comparisions will work correctly.
|
||||
;; Convert it to a LocalDateTime, in the report timezone, so comparisons will work correctly.
|
||||
;;
|
||||
;; See also — https://dev.mysql.com/doc/refman/5.5/en/datetime.html
|
||||
;;
|
||||
@@ -767,7 +767,7 @@
|
||||
;; There is currently no way to tell whether the column is the result of a `timediff()` call (i.e., a duration) or a
|
||||
;; normal `LocalTime` -- JDBC doesn't have interval/duration type enums. `java.time.LocalTime`only accepts values of
|
||||
;; hour between 0 and 23 (inclusive). The MariaDB JDBC driver's implementations of `(.getObject rs i
|
||||
;; java.time.LocalTime)` will throw Exceptions theses cases.
|
||||
;; java.time.LocalTime)` will throw Exceptions in these cases.
|
||||
;;
|
||||
;; Thus we should attempt to fetch temporal results the normal way and fall back to string representations for cases
|
||||
;; where the values are unparseable.
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
constraint-name)
|
||||
(reduced nil))))
|
||||
[[] nil nil]
|
||||
(jdbc/reducible-query jdbc-spec sql-args {:identifers identity, :transaction? false})))))
|
||||
(jdbc/reducible-query jdbc-spec sql-args {:transaction? false})))))
|
||||
|
||||
(defmethod sql-jdbc.actions/maybe-parse-sql-error [:mysql driver-api/violate-not-null-constraint]
|
||||
[_driver error-type _database _action-type error-message]
|
||||
@@ -228,7 +228,7 @@
|
||||
select-sql-args (select-created-row-sql-args driver create-hsql pks insert_id)
|
||||
query-results (jdbc/query jdbc-spec
|
||||
select-sql-args
|
||||
{:identifiers identity, :transaction? false, :keywordize? false})]
|
||||
{:transaction? false, :keywordize? false})]
|
||||
(if (next query-results)
|
||||
(log/warn "cannot identify row inserted by" create-hsql "using results" results)
|
||||
(first query-results))))
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
sql-args ["select column_name from information_schema.constraint_column_usage where constraint_name = ?" constraint-name]]
|
||||
(into []
|
||||
(map :column_name)
|
||||
(jdbc/reducible-query jdbc-spec sql-args {:identifers identity, :transaction? false}))))
|
||||
(jdbc/reducible-query jdbc-spec sql-args {:transaction? false}))))
|
||||
|
||||
(defmethod sql-jdbc.actions/maybe-parse-sql-error [:postgres driver-api/violate-not-null-constraint]
|
||||
[_driver error-type _database _action-type error-message]
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
(defmulti ->prepared-substitution
|
||||
"Returns a `PreparedStatementSubstitution` (see schema below) for `x` and the given driver. This allows driver
|
||||
specific parameters and SQL replacement text (usually just ?). The param value is already prepared and ready for
|
||||
inlcusion in the query, such as what's needed for SQLite and timestamps."
|
||||
inclusion in the query, such as what's needed for SQLite and timestamps."
|
||||
{:added "0.34.0" :arglists '([driver x])}
|
||||
(fn [driver x] [(driver/dispatch-on-initialized-driver driver) (class x)])
|
||||
:hierarchy #'driver/hierarchy)
|
||||
@@ -288,7 +288,7 @@
|
||||
(field->clause field {:temporal-unit (align-temporal-unit-with-param-type-and-value driver field param-type value)}))
|
||||
|
||||
(mu/defn- field->identifier :- driver-api/schema.common.non-blank-string
|
||||
"Return an approprate snippet to represent this `field` in SQL given its param type.
|
||||
"Return an appropriate snippet to represent this `field` in SQL given its param type.
|
||||
For non-date Fields, this is just a quoted identifier; for dates, the SQL includes appropriately bucketing based on
|
||||
the `param-type`."
|
||||
[driver field param-type value]
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
|
||||
That requires:
|
||||
|
||||
- Removal of traling comments (after the semicolon).
|
||||
- Removal of trailing comments (after the semicolon).
|
||||
- Removing the semicolon(s).
|
||||
- Squashing whitespace at the end of the string and replacinig it with newline. This is required in case some
|
||||
comments were preceding semicolon.
|
||||
@@ -243,7 +243,7 @@
|
||||
Used in implementations of ->integer."
|
||||
[driver value]
|
||||
;; value can be either string or float
|
||||
;; if it's a float, coversion to float does nothing
|
||||
;; if it's a float, conversion to float does nothing
|
||||
;; if it's a string, we can't round, so we need to convert to float first
|
||||
(->> value
|
||||
(->float driver)
|
||||
@@ -269,7 +269,7 @@
|
||||
(inline-value driver/*driver* x)
|
||||
(honey.sql.protocols/sqlize x)))
|
||||
|
||||
;;; Replace the implentation of [[honey.sql/sqlize-value]] with one that hands off to [[inline-value]] if driver is
|
||||
;;; Replace the implementation of [[honey.sql/sqlize-value]] with one that hands off to [[inline-value]] if driver is
|
||||
;;; bound. This way we can have driver-specific inline behavior. Monkey-patching private functions like this is a little
|
||||
;;; questionable for sure but I think it's justified here since there is on other way to consistently guarantee that we
|
||||
;;; hand off to [[sqlize-value]] when compiling something inline.
|
||||
@@ -332,7 +332,7 @@
|
||||
|
||||
(defmethod date [:sql :week-of-year]
|
||||
[driver _ expr]
|
||||
;; Some DBs truncate when doing integer division, therefore force float arithmetics
|
||||
;; Some DBs truncate when doing integer division, therefore force float arithmetic
|
||||
(->honeysql driver [:ceil (compiled (h2x// (date driver :day-of-year (date driver :week expr)) 7.0))]))
|
||||
|
||||
(defmethod date [:sql :month-of-year] [_driver _ expr] (h2x/month expr))
|
||||
@@ -351,7 +351,7 @@
|
||||
:hierarchy #'driver/hierarchy)
|
||||
|
||||
(defn- days-till-start-of-first-full-week
|
||||
"Takes a datetime expession, return a HoneySQL form
|
||||
"Takes a datetime expression, return a HoneySQL form
|
||||
that calculate how many days from the Jan 1st till the start of `first full week`.
|
||||
|
||||
A full week is a week that contains 7 days in the same year.
|
||||
@@ -1314,7 +1314,7 @@
|
||||
[:offset (options :guard :name) _expr _n]
|
||||
(->honeysql driver (h2x/identifier :field-alias (:name options)))
|
||||
|
||||
;; for everything else just use the name of the aggregation as an identifer, e.g. `:sum`
|
||||
;; for everything else just use the name of the aggregation as an identifier, e.g. `:sum`
|
||||
;;
|
||||
;; TODO -- I don't think we will ever actually get to this anymore because everything should have been given a name
|
||||
;; by [[metabase.query-processor.middleware.pre-alias-aggregations]]
|
||||
@@ -1385,7 +1385,7 @@
|
||||
|
||||
;; TODO -- this name is a bit of a misnomer since it also handles `:aggregation` and `:expression` clauses.
|
||||
(mu/defn field-clause->alias :- some?
|
||||
"Generate HoneySQL for an approriate alias (e.g., for use with SQL `AS`) for a `:field`, `:expression`, or
|
||||
"Generate HoneySQL for an appropriate alias (e.g., for use with SQL `AS`) for a `:field`, `:expression`, or
|
||||
`:aggregation` clause of any type, or `nil` if the Field should not be aliased. By default uses the
|
||||
`::add/desired-alias` key in the clause options.
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
The methods should returns a map of:
|
||||
- type: the error type. Check [[metabase.actions.error]] for the full list
|
||||
- message: a nice message summarized of what went wrong
|
||||
- errors: a map from field-name => sepcific error message. This is used by UI to display per fields error
|
||||
- errors: a map from field-name => specific error message. This is used by UI to display per fields error
|
||||
If non per-column error is available, returns an empty map.
|
||||
|
||||
Or return `nil` if the parser doesn't match."
|
||||
@@ -458,7 +458,7 @@
|
||||
(let [db-id (u/the-id database)
|
||||
driver (:engine database)
|
||||
table-id (get-in query [:query :source-table])
|
||||
;; Check that we have a PK before we insert thte data, because we'd need this to query the return data.
|
||||
;; Check that we have a PK before we insert the data, because we'd need this to query the return data.
|
||||
_ (assert-pk! db-id table-id)
|
||||
{:keys [from]} (mbql-query->raw-hsql driver query)
|
||||
create-hsql (-> {:insert-into (first from)
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
:always (assoc :subname (conn-str-with-additional-opts connection-string seperator-style additional-options)))))
|
||||
|
||||
(defn additional-options->map
|
||||
"Attempts to parse the entires within the `additional-options` string into a map of keys to values. `separator-style`
|
||||
"Attempts to parse the entries within the `additional-options` string into a map of keys to values. `separator-style`
|
||||
works as in the other functions in this namespace (since it influences the separator that appears between pairs).
|
||||
|
||||
`opt-name-val-separator?` is an optional parameter that indicates the string that appears between keys and values. If
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
[(driver/dispatch-on-initialized-driver driver) (class object)])
|
||||
:hierarchy #'driver/hierarchy)
|
||||
|
||||
;; TODO -- maybe like [[do-with-connection-with-options]] we should replace [[prepared-statment]] and [[statement]]
|
||||
;; TODO -- maybe like [[do-with-connection-with-options]] we should replace [[prepared-statement]] and [[statement]]
|
||||
;; with `do-with-prepared-statement` and `do-with-statement` methods -- that way you can't accidentally forget to wrap
|
||||
;; things in a `try-catch` and call `.close` (metabase#40010)
|
||||
|
||||
@@ -753,7 +753,7 @@
|
||||
|
||||
; Combines the original SQL query with query remarks. Most databases using sql-jdbc based drivers support prepending the
|
||||
; remark to the SQL statement, so we have it as a default. However, some drivers do not support it, so we allow it to
|
||||
; be overriden.
|
||||
; be overridden.
|
||||
(defmethod inject-remark :default
|
||||
[_ sql remark]
|
||||
(str "-- " remark "\n" sql))
|
||||
@@ -798,9 +798,9 @@
|
||||
(let [rsmeta (.getMetaData rs)
|
||||
results-metadata {:cols (column-metadata driver rsmeta)}]
|
||||
(try (respond results-metadata (reducible-rows driver rs rsmeta (driver-api/canceled-chan)))
|
||||
;; Following cancels the statment on the dbms side.
|
||||
;; Following cancels the statement on the dbms side.
|
||||
;; It avoids blocking `.close` call, in case we reduced the results subset eg. by means of
|
||||
;; [[metabase.query-processor.middleware.limit/limit-xform]] middleware, while statment is still
|
||||
;; [[metabase.query-processor.middleware.limit/limit-xform]] middleware, while statement is still
|
||||
;; in progress. This problem was encountered on Redshift. For details see the issue #39018.
|
||||
;; It also handles situation where query is canceled through [[driver-api/canceled-chan]] (#41448).
|
||||
(finally
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
{:pre [(string? sql)]}
|
||||
(with-open [stmt (sql-jdbc.sync.common/prepare-statement driver conn sql params)]
|
||||
;; attempting to execute the SQL statement will throw an Exception if we don't have permissions; otherwise it will
|
||||
;; truthy wheter or not it returns a ResultSet, but we can ignore that since we have enough info to proceed at
|
||||
;; truthy whether or not it returns a ResultSet, but we can ignore that since we have enough info to proceed at
|
||||
;; this point.
|
||||
(doto stmt
|
||||
(.setQueryTimeout *select-probe-query-timeout-seconds*)
|
||||
@@ -109,7 +109,7 @@
|
||||
|
||||
(let [;; Let's try to ensure the connection is not just open but also valid.
|
||||
;; Snowflake closes the connection but doesn't set it as closed in the object,
|
||||
;; so we must explicitely check if it's valid so that subsequent calls to [[sql-jdbc.execute/try-ensure-open-conn!]]
|
||||
;; so we must explicitly check if it's valid so that subsequent calls to [[sql-jdbc.execute/try-ensure-open-conn!]]
|
||||
;; will obtain a new connection
|
||||
is-open (sql-jdbc.execute/is-conn-open? conn :check-valid? true)
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
"Build a nested map of schema -> table -> set of permissions from current user table privileges.
|
||||
There are 2 permissions:
|
||||
- :select - self-explained
|
||||
- :write - must have insert, update, and delete permisisons. used for table data editing"
|
||||
- :write - must have insert, update, and delete permissions. used for table data editing"
|
||||
[driver conn]
|
||||
(->> (sql-jdbc.sync.interface/current-user-table-privileges driver {:connection conn})
|
||||
(reduce (fn [acc {:keys [schema table select insert update delete]}]
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
;;
|
||||
;; 3. Filter out any duplicates between the two methods using `m/distinct-by`.
|
||||
(let [has-fields-without-type-info? (volatile! false)
|
||||
;; intented to fix syncing dynamic tables for snowflake.
|
||||
;; intended to fix syncing dynamic tables for snowflake.
|
||||
;; currently there is a bug in snowflake jdbc (snowflake#1574) in which it doesn't return columns for dynamic tables
|
||||
jdbc-returns-no-field? (volatile! true)
|
||||
jdbc-metadata (eduction
|
||||
@@ -463,7 +463,7 @@
|
||||
(filter #(nil? (:filter_condition %)))
|
||||
(jdbc/reducible-result-set index-info-rs {})))
|
||||
(keep (fn [[index-name idx-values]]
|
||||
;; we only sync columns that are either singlely indexed or is the first key in a composite index
|
||||
;; we only sync columns that are either singly indexed or is the first key in a composite index
|
||||
(when-let [column-name (some :column_name (sort-by :ordinal_position idx-values))]
|
||||
{:type :normal-column-index
|
||||
:index-name index-name
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
|
||||
(defmulti fallback-metadata-query
|
||||
"SELECT columns from a given table so we can get column metadata. By default doesn't return any rows. This can be
|
||||
overriden because SQLite is silly and only returns column information for views if the query returns a non-zero
|
||||
overridden because SQLite is silly and only returns column information for views if the query returns a non-zero
|
||||
number of rows.
|
||||
|
||||
(fallback-metadata-query :postgres \"my_database\" \"public\" \"my_table\")
|
||||
|
||||
@@ -185,7 +185,7 @@
|
||||
api/*current-user*)
|
||||
|
||||
(defn canceled-chan
|
||||
"If this channel is bount you can check if it has received a message
|
||||
"If this channel is bound you can check if it has received a message
|
||||
to see if the query has been canceled."
|
||||
[]
|
||||
qp.pipeline/*canceled-chan*)
|
||||
|
||||
@@ -452,7 +452,7 @@
|
||||
(catch Throwable e
|
||||
(let [e (ex-info (.getMessage e)
|
||||
{:card-id (u/the-id card)
|
||||
:card-params (:parametres card)
|
||||
:card-params (:parameters card)
|
||||
:allowed-param-slugs embedding-params
|
||||
:slug->id slug->id
|
||||
:id->slug id->slug
|
||||
@@ -497,7 +497,7 @@
|
||||
(catch Throwable e
|
||||
(let [e (ex-info (.getMessage e)
|
||||
{:card-id (u/the-id card)
|
||||
:card-params (:parametres card)
|
||||
:card-params (:parameters card)
|
||||
:allowed-param-slugs embedding-params
|
||||
:slug->id slug->id
|
||||
:id->slug id->slug
|
||||
|
||||
@@ -228,7 +228,7 @@
|
||||
(format-timestring timezone-id temporal-str (dissoc col :unit) viz-settings)
|
||||
;; We're making an assumption when we bottom out here that the string is compatible with this default format,
|
||||
;; 'MMMM d, yyyy'. If the time string isn't compatible with this format, we just return the string.
|
||||
;; This is not likely to happen IRL since you generally have a useful unit or know the type of the colum. A failure
|
||||
;; This is not likely to happen IRL since you generally have a useful unit or know the type of the column. A failure
|
||||
;; mode that can be reproduced in test is trying to format a time string (e.g.'15:30:45Z') when the column has no
|
||||
;; type information (e.g. a semantic or effective type of `:type/Time`).
|
||||
(let [date-format (post-process-date-style (or date-style "MMMM d, yyyy") viz-settings)]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns metabase.legacy-mbql.jvm-util
|
||||
"This namespace contains functionality that is not compatible with js, hence can not be stored in correspoding
|
||||
"This namespace contains functionality that is not compatible with js, hence can not be stored in corresponding
|
||||
cljc ns, ie. [[metabase.legacy-mbql.util]]."
|
||||
{:deprecated "0.57.0"}
|
||||
(:require
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
"datetime" ::lib.schema.literal/datetime
|
||||
"unit" ::DateTimeUnit)]])
|
||||
|
||||
;; almost exactly the same as `absolute-datetime`, but generated in some sitations where the literal in question was
|
||||
;; almost exactly the same as `absolute-datetime`, but generated in some situations where the literal in question was
|
||||
;; clearly a time (e.g. "08:00:00.000") and/or the Field derived from `:type/Time` and/or the unit was a
|
||||
;; time-bucketing unit
|
||||
(defclause time
|
||||
@@ -1962,7 +1962,7 @@
|
||||
{:type "A join should not include :type"
|
||||
:filter "A join should not have top-level 'inner' query keys like :filter"
|
||||
:breakout "A join should not have top-level 'inner' query keys like :breakout"
|
||||
:aggreggation "A join should not have top-level 'inner' query keys like :aggreggation"
|
||||
:aggregation "A join should not have top-level 'inner' query keys like :aggregation"
|
||||
:expressions "A join should not have top-level 'inner' query keys like :expressions"
|
||||
:joins "A join should not have top-level 'inner' query keys like :joins"
|
||||
:ident ":ident is deprecated and should not be included in joins"})])
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
tag)))
|
||||
|
||||
(defn one-of*
|
||||
"Interal impl of `one-of` macro."
|
||||
"Internal impl of `one-of` macro."
|
||||
[& schemas]
|
||||
(into
|
||||
[:multi {:dispatch effective-clause-tag
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns metabase.legacy-mbql.util
|
||||
"Utilitiy functions for working with MBQL queries.
|
||||
"Utility functions for working with MBQL queries.
|
||||
|
||||
DEPRECATED: Use [[metabase.lib.core]] for MBQL manipulation in all new code."
|
||||
{:deprecated "0.57.0"}
|
||||
|
||||
@@ -361,7 +361,7 @@
|
||||
(:columns aggregation-operator))
|
||||
|
||||
(mu/defn available-aggregation-operators :- [:maybe [:sequential OperatorWithColumns]]
|
||||
"Returns the available aggegation operators for the stage with `stage-number` of `query`.
|
||||
"Returns the available aggregation operators for the stage with `stage-number` of `query`.
|
||||
If `stage-number` is omitted, uses the last stage."
|
||||
([query]
|
||||
(available-aggregation-operators query -1))
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
[:num-bins ::lib.schema.binning/num-bins]]])
|
||||
|
||||
(mu/defn- resolve-default-strategy :- ResolvedStrategy
|
||||
"Determine the approprate strategy & options to use when `:default` strategy was specified."
|
||||
"Determine the appropriate strategy & options to use when `:default` strategy was specified."
|
||||
[metadata-providerable :- ::lib.schema.metadata/metadata-providerable
|
||||
column :- ::lib.schema.metadata/column
|
||||
min-value :- number?
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
[:maybe [:sequential {:min 1} ::column]])
|
||||
|
||||
(def ^:private ^:dynamic *card-metadata-columns-card-ids*
|
||||
"Used to track the ID of Cards we're resolving columns for, to avoid inifinte recursion for Cards that have circular
|
||||
"Used to track the ID of Cards we're resolving columns for, to avoid infinite recursion for Cards that have circular
|
||||
references between one another."
|
||||
#{})
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
- One or two category breakouts: no `:location` pivot. (`pivot` mode)
|
||||
- If all these conditions fail, no pivots are allowed and the pivot drill should not be returned.
|
||||
|
||||
This function encodes all these rules, returning a (possibly emtpy) set of permitted types."
|
||||
This function encodes all these rules, returning a (possibly empty) set of permitted types."
|
||||
[query :- ::lib.schema/query
|
||||
stage-number :- :int]
|
||||
(case (->> (lib.breakout/breakouts query stage-number)
|
||||
|
||||
@@ -117,8 +117,8 @@
|
||||
(or (lib.binning/resolve-bin-width query column value)
|
||||
(lib.binning/resolve-bin-width
|
||||
query
|
||||
;; One of the "superflous" options is ::lib.field/binning, which we want to preserve here.
|
||||
(lib.underlying/top-level-column query column :rename-superflous-options? false)
|
||||
;; One of the "superfluous" options is ::lib.field/binning, which we want to preserve here.
|
||||
(lib.underlying/top-level-column query column :rename-superfluous-options? false)
|
||||
value))]
|
||||
(case (:strategy binning)
|
||||
(:num-bins :default)
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
[query :- ::lib.schema/query
|
||||
stage-number :- :int
|
||||
{:keys [row], :as context} :- ::lib.schema.drill-thru/context]
|
||||
(let [;; First check returned columns in case we breakout by lat/lon so we maintain the binning, othwerwise check visible.
|
||||
(let [;; First check returned columns in case we breakout by lat/lon so we maintain the binning, otherwise check visible.
|
||||
[lat-column lon-column] (some
|
||||
(fn [columns]
|
||||
(when-let [lat-column (m/find-first lib.types.isa/latitude? columns)]
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
"Modify expression in a way, that its resulting [[expression-parts]] are digestable by filter picker.
|
||||
|
||||
Current filter picker implementation is unable to handle expression parts of expressions of a form
|
||||
`[:= {...} [:field {:temporal-unit :week} 11] \"2024-05-12\"]` -- expresions that check for equality of a column
|
||||
`[:= {...} [:field {:temporal-unit :week} 11] \"2024-05-12\"]` -- expressions that check for equality of a column
|
||||
with `:temporal-unit` set to value other than `:day` or `:minute` to some date time value.
|
||||
|
||||
To mitigate that expressions are converted to `:between` form which is handled correctly by filter picker. For more
|
||||
|
||||
@@ -390,10 +390,10 @@
|
||||
(and (isa? effective-type :type/Number)
|
||||
(not (isa? semantic-type :Relation/*))) (lib.binning/numeric-binning-strategies))]
|
||||
;; TODO: Include the time and date binning strategies too
|
||||
(for [strat strategies]
|
||||
(cond-> strat
|
||||
(for [strategy strategies]
|
||||
(cond-> strategy
|
||||
(or (:lib/original-binning field-metadata) existing) (dissoc :default)
|
||||
(lib.binning/strategy= strat existing) (assoc :selected true))))
|
||||
(lib.binning/strategy= strategy existing) (assoc :selected true))))
|
||||
[]))
|
||||
|
||||
(defmethod lib.ref/ref-method :field
|
||||
@@ -705,7 +705,7 @@
|
||||
{:query query
|
||||
:stage stage-number
|
||||
:source source}))
|
||||
;; Default case: do nothing and return the query unchaged.
|
||||
;; Default case: do nothing and return the query unchanged.
|
||||
;; Generate a warning - we should aim to capture every `:source/*` value above.
|
||||
(do
|
||||
(log/warnf "Cannot remove-field with unknown source %s" (pr-str source))
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
:name :lib/ref-name})
|
||||
|
||||
(defn- opts-fn-inherited-temporal-unit
|
||||
"`:inherited-temporal-unit` is transfered from `:temporal-unit` ref option only when
|
||||
"`:inherited-temporal-unit` is transferred from `:temporal-unit` ref option only when
|
||||
the [[lib.metadata.calculation/*propagate-binning-and-bucketing*]] is truthy, i.e. bound.
|
||||
|
||||
TODO (Cam 6/18/25) -- that DOES NOT seem to be how it actually works. (Other documentation here was not mine.)
|
||||
@@ -546,7 +546,7 @@
|
||||
|
||||
;; we maybe have incorrectly used a field name ref when we should have used a field ID ref.
|
||||
;;
|
||||
;; TODO (Cam 8/15/25) -- what happpens if this field is marked inactive? It won't come back from
|
||||
;; TODO (Cam 8/15/25) -- what happens if this field is marked inactive? It won't come back from
|
||||
;; `returned-columns`... we'd get fallback metadata, right?
|
||||
(and source-table-id
|
||||
(string? id-or-name))
|
||||
|
||||
@@ -412,7 +412,7 @@
|
||||
|
||||
(mu/defn filters :- [:maybe [:ref ::lib.schema/filters]]
|
||||
"Returns the current filters in stage with `stage-number` of `query`.
|
||||
If `stage-number` is omitted, the last stage is used. Logicaly, the
|
||||
If `stage-number` is omitted, the last stage is used. Logically, the
|
||||
filter attached to the query is the conjunction of the expressions
|
||||
in the returned list. If the returned list is empty, then there is no
|
||||
filter attached to the query.
|
||||
@@ -530,7 +530,7 @@
|
||||
|
||||
(mu/defn filter-parts :- FilterParts
|
||||
"Return the parts of the filter clause `a-filter-clause` in query `query` at stage `stage-number`.
|
||||
Might obsolate [[filter-operator]]."
|
||||
Might obsolete [[filter-operator]]."
|
||||
([query a-filter-clause]
|
||||
(filter-parts query -1 a-filter-clause))
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@
|
||||
(lib.util.match/replace expr
|
||||
[:does-not-contain opts whole & parts]
|
||||
(-> (lib.filter/not (-> (apply lib.filter/contains whole parts)
|
||||
;; need to preserve stuff like `:case-sensitve`. Prefer values in each clause over `opts`
|
||||
;; need to preserve stuff like `:case-sensitive`. Prefer values in each clause over `opts`
|
||||
;; so we don't get duplicate UUIDs
|
||||
(lib.options/update-options #(merge opts %))))
|
||||
(merge-options opts))))
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
frontend using functions like [[join-condition-operators]], [[join-condition-lhs-columns]], and
|
||||
[[join-condition-rhs-columns]].
|
||||
|
||||
LHS and RHS expressions can be aribtrary and not only `:field` references. The LHS expression will normally contain
|
||||
LHS and RHS expressions can be arbitrary and not only `:field` references. The LHS expression will normally contain
|
||||
columns from [[join-condition-lhs-columns]], and the RHS expression from [[join-condition-rhs-columns]]."
|
||||
[condition :- [:maybe ::lib.schema.expression/boolean]]
|
||||
(when condition
|
||||
@@ -578,11 +578,11 @@
|
||||
(defn- add-alias-to-condition
|
||||
[query stage-number condition join-alias home-cols join-cols]
|
||||
(let [condition (add-alias-to-join-refs query stage-number condition join-alias join-cols)]
|
||||
;; Sometimes conditions have field references which cannot be unambigously
|
||||
;; Sometimes conditions have field references which cannot be unambiguously
|
||||
;; assigned to one of the sides. The following code tries to deal with
|
||||
;; 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,
|
||||
;; so we break ties by looking at the poisition of the field reference.
|
||||
;; so we break ties by looking at the position of the field reference.
|
||||
(lib.util.match/replace condition
|
||||
[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)
|
||||
@@ -702,7 +702,7 @@
|
||||
(get a-join :strategy :left-join))
|
||||
|
||||
(mu/defn join-strategy :- ::lib.schema.join/strategy.option
|
||||
"Get the strategy (type) of a given join, as a `:option/join.strategy` map. If `:stategy` is unspecified, returns
|
||||
"Get the strategy (type) of a given join, as a `:option/join.strategy` map. If `:strategy` is unspecified, returns
|
||||
the default, left join."
|
||||
[a-join :- ::lib.join.util/partial-join]
|
||||
(raw-join-strategy->strategy-option (raw-join-strategy a-join)))
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
|
||||
(defn ^:export as-returned
|
||||
"When a query has aggregations in stage `N`, there's an important difference between adding an expression to stage `N`
|
||||
(with access to the colums before aggregation) or adding it to stage `N+1` (with access to the aggregations and
|
||||
(with access to the columns before aggregation) or adding it to stage `N+1` (with access to the aggregations and
|
||||
breakouts).
|
||||
|
||||
Given `a-query` and `stage-number`, this returns a **JS object** with `query` and `stageIndex` keys, for working with
|
||||
@@ -2189,7 +2189,7 @@
|
||||
(lib.core/database-id a-query))
|
||||
|
||||
(defn ^:export join-condition-update-temporal-bucketing
|
||||
"Updates the provided `join-condition` so both the LHS and RHS columns have the provied temporal bucketing option.
|
||||
"Updates the provided `join-condition` so both the LHS and RHS columns have the provided temporal bucketing option.
|
||||
|
||||
`join-condition` must be a *standard join condition*, meaning it's in the form constructed by the query builder UI,
|
||||
where the LHS is a column in the outer query and RHS is a column from the joinable.
|
||||
|
||||
@@ -412,7 +412,7 @@
|
||||
(let [parse-card-ignoring-plain-object (parse-object-fn :card {:use-plain-object? false})
|
||||
parse-card (parse-object-fn :card)]
|
||||
;; The question objects might not contain the fields so we merge them
|
||||
;; in from the table matadata.
|
||||
;; in from the table metadata.
|
||||
(merge
|
||||
(-> metadata
|
||||
(object-get "tables")
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
[metabase.lib.ref :as lib.ref]
|
||||
[metabase.lib.schema :as lib.schema]
|
||||
[metabase.lib.schema.common :as lib.schema.common]
|
||||
[metabase.lib.schema.expression :as lib.schema.expresssion]
|
||||
[metabase.lib.schema.expression :as lib.schema.expression]
|
||||
[metabase.lib.schema.metadata :as lib.schema.metadata]
|
||||
[metabase.lib.schema.temporal-bucketing :as lib.schema.temporal-bucketing]
|
||||
[metabase.lib.types.isa :as lib.types.isa]
|
||||
@@ -183,7 +183,7 @@
|
||||
(defmulti type-of-method
|
||||
"Calculate the effective type of something. This differs from [[metabase.lib.schema.expression/type-of]] in that it is
|
||||
called with a query/MetadataProvider and a stage number, allowing us to fully resolve information and return
|
||||
complete, unambigous type information. Default implementation calls [[metabase.lib.schema.expression/type-of]]."
|
||||
complete, unambiguous type information. Default implementation calls [[metabase.lib.schema.expression/type-of]]."
|
||||
{:arglists '([query stage-number expr])}
|
||||
(fn [_query _stage-number expr]
|
||||
(lib.dispatch/dispatch-value expr))
|
||||
@@ -223,7 +223,7 @@
|
||||
|
||||
(defmethod type-of-method :default
|
||||
[_query _stage-number expr]
|
||||
(lib.schema.expresssion/type-of expr))
|
||||
(lib.schema.expression/type-of expr))
|
||||
|
||||
;;; for MBQL clauses whose type is the same as the type of the first arg. Also used
|
||||
;;; for [[metabase.lib.schema.expression/type-of]].
|
||||
@@ -235,7 +235,7 @@
|
||||
[query stage-number [_tag _opts expr :as clause]]
|
||||
(if (string? expr)
|
||||
;; If a string, get the type filtered by this expression (eg. `:datetime-add`).
|
||||
(lib.schema.expresssion/type-of clause)
|
||||
(lib.schema.expression/type-of clause)
|
||||
;; Otherwise, just get the type of this first arg.
|
||||
(type-of query stage-number expr)))
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
`:id` and `:name` are mutually exclusive.
|
||||
|
||||
When fetching metadata that can be inactive/archived/hidden, only active/unarchived/unhidden objects are fetched
|
||||
unless `:id` or `:name` is specifed."
|
||||
unless `:id` or `:name` is specified."
|
||||
[:and
|
||||
[:map
|
||||
{:closed true}
|
||||
@@ -129,7 +129,7 @@
|
||||
side-effects (to warm the cache).
|
||||
|
||||
When fetching metadata that can be inactive/archived/hidden, only active/unarchived/unhidden objects are fetched
|
||||
unless `:id` or `:name` is specifed.")
|
||||
unless `:id` or `:name` is specified.")
|
||||
|
||||
(setting [metadata-provider setting-key]
|
||||
"Return the value of the given Metabase setting with keyword `setting-name`."))
|
||||
@@ -388,7 +388,7 @@
|
||||
|
||||
(#?(:clj p/defprotocol+ :cljs defprotocol) InvocationTracker
|
||||
"Optional. A protocol for a MetadataProvider that records the arguments of method invocations during query execution.
|
||||
This is useful for tracking which metdata ids were used during a query execution. The main purpose of this is to power
|
||||
This is useful for tracking which metadata ids were used during a query execution. The main purpose of this is to power
|
||||
updating card.last_used_at during query execution. see [[metabase.query-processor.middleware.update-used-cards/update-used-cards!]]"
|
||||
(invoked-ids [this metadata-type]
|
||||
"Get all invoked ids of a metadata type thus far."))
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
* [[no-value]]
|
||||
|
||||
* A map contianing the value and type info for the value, e.g.
|
||||
* A map containing the value and type info for the value, e.g.
|
||||
|
||||
{:type :date/single
|
||||
:value #t \"2019-09-20T19:52:00.000-07:00\"}
|
||||
|
||||
@@ -284,7 +284,7 @@
|
||||
[:= :default]
|
||||
[:ref ::temporal-bucketing/unit.date-time.interval]])
|
||||
|
||||
;;; TODO (Cam 7/16/25) -- I think unit is rewuired unless `n` is `:current`
|
||||
;;; TODO (Cam 7/16/25) -- I think unit is required unless `n` is `:current`
|
||||
(mbql-clause/define-catn-mbql-clause :relative-datetime :- :type/DateTime
|
||||
[:n [:schema [:ref ::relative-datetime.amount]]]
|
||||
[:unit [:? [:schema [:ref ::relative-datetime.unit]]]])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns metabase.lib.schema.parameter
|
||||
"`:parameters` specify the *values* of parameters previously definied for a Dashboard or Card (native query template
|
||||
"`:parameters` specify the *values* of parameters previously defined for a Dashboard or Card (native query template
|
||||
tag parameters.) See [[metabase.lib.schema.template-tag]] above for more information on the later.
|
||||
|
||||
There are three things called 'type' in play when we talk about parameters and template tags.
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
(def ^:private TopLevelColumnOptions
|
||||
[:map
|
||||
{:closed true}
|
||||
[:rename-superflous-options? {:optional true} :boolean]])
|
||||
[:rename-superfluous-options? {:optional true} :boolean]])
|
||||
|
||||
(mu/defn top-level-column :- [:maybe ::lib.schema.metadata/column]
|
||||
"Given a column, returns the \"top-level\" equivalent.
|
||||
@@ -72,10 +72,10 @@
|
||||
Returns nil if the column can't be traced back to the top-level query."
|
||||
([query :- ::lib.schema/query
|
||||
column :- ::lib.schema.metadata/column]
|
||||
(top-level-column query column :rename-superflous-options? true))
|
||||
(top-level-column query column :rename-superfluous-options? true))
|
||||
([query :- ::lib.schema/query
|
||||
column :- ::lib.schema.metadata/column
|
||||
& {:keys [rename-superflous-options?]} :- TopLevelColumnOptions]
|
||||
& {:keys [rename-superfluous-options?]} :- TopLevelColumnOptions]
|
||||
(let [top-query (top-level-query query)]
|
||||
(if (= query top-query)
|
||||
column ;; Unchanged if this is already a top-level query. That includes keeping the "superfluous" options!
|
||||
@@ -83,7 +83,7 @@
|
||||
column column]
|
||||
(if (= query top-query)
|
||||
(cond-> column
|
||||
rename-superflous-options?
|
||||
rename-superfluous-options?
|
||||
;; Once we've found it, rename superfluous options, unless disabled by the caller, because under normal
|
||||
;; circumstances, you will not need them. On the off chance you do need them, they'll still be available.
|
||||
(set/rename-keys {::lib.field/temporal-unit ::temporal-unit
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
[net.cgrand.macrovich :as macros]))
|
||||
|
||||
(defn- generate-pattern
|
||||
"Generate a single approprate pattern for use with core.match based on the `pattern` input passed into `match` or
|
||||
"Generate a single appropriate pattern for use with core.match based on the `pattern` input passed into `match` or
|
||||
`replace`."
|
||||
[pattern]
|
||||
(cond
|
||||
@@ -152,7 +152,7 @@
|
||||
automatically recurse, use a catch-all pattern (such as `_`). Our macro implementation will optimize out this
|
||||
`:else` clause if the last pattern is `_`
|
||||
|
||||
### Returing something other than the exact match with result body
|
||||
### 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
|
||||
@@ -187,7 +187,7 @@
|
||||
;; -> [[:field 1 nil]]"
|
||||
{:style/indent :defn}
|
||||
[x & patterns-and-results]
|
||||
;; Actual implementation of these macros is in `mbql.util.match`. They're in a seperate namespace because they have
|
||||
;; 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))
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
[] form))))
|
||||
|
||||
(defn replace-in-collection
|
||||
"Inernal impl for `replace`. Recursively replace values in a collection using a `replace-fn`."
|
||||
"Internal impl for `replace`. Recursively replace values in a collection using a `replace-fn`."
|
||||
[replace-fn clause-parents form]
|
||||
(cond
|
||||
(map? form)
|
||||
|
||||
@@ -571,7 +571,7 @@
|
||||
[[application-database-metadata-provider]] will use it for caching the `MetadataProvider` for each `database-id`
|
||||
over the lifespan of this binding.
|
||||
|
||||
This is useful for an API request, or group fo API requests like a dashboard load, to reduce appdb traffic."
|
||||
This is useful for an API request, or group for API requests like a dashboard load, to reduce appdb traffic."
|
||||
nil)
|
||||
|
||||
(defn metadata-provider-cache
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
"Marks PersistedInfo as `deletable` or `off`, these will at some point be cleaned up by the PersistPrune task.
|
||||
|
||||
`deletable` will wipe out all trace of persisted-info and allow them to be turned back on by automatic processes
|
||||
use when you are disabling peristence at a high level.
|
||||
use when you are disabling persistence at a high level.
|
||||
`off` will ensure automatic processes do not pick up these up and re-enable."
|
||||
([conditions-map]
|
||||
(mark-for-pruning! conditions-map "deletable"))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
There are currently two implementations of humanization logic, previously three.
|
||||
Which implementation is used is determined by the Setting `humanization-strategy`.
|
||||
`:simple`, which merely replaces underscores and dashes with spaces, and `:none`,
|
||||
which predictibly is merely an identity function that does nothing to the results.
|
||||
which predictably is merely an identity function that does nothing to the results.
|
||||
|
||||
There used to also be `:advanced`, which was the default until enough customers
|
||||
complained that we first fixed it and then the fix wasn't good enough so we removed it."
|
||||
@@ -29,7 +29,7 @@
|
||||
(name->human-readable-name \"cool_toucans\") ;-> \"Cool Toucans\"
|
||||
;; this is the same as:
|
||||
(name->human-readable-name (humanization-strategy) \"cool_toucans\") ;-> \"Cool Toucans\"
|
||||
;; specifiy a different strategy:
|
||||
;; specify a different strategy:
|
||||
(name->human-readable-name :none \"cool_toucans\") ;-> \"cool_toucans\""
|
||||
([s]
|
||||
(name->human-readable-name (humanization-strategy) s))
|
||||
|
||||
@@ -277,7 +277,7 @@
|
||||
(defn transform-validator
|
||||
"Given a transform, returns a transform that call `assert-fn` on the \"out\" value.
|
||||
|
||||
E.g: A keyword transfomer that throw an error if the value is not namespaced
|
||||
E.g: A keyword transformer that throw an error if the value is not namespaced
|
||||
(transform-validator
|
||||
transform-keyword (fn [x]
|
||||
(when-not (-> x namespace some?)
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
mi/dispatch-on-model)
|
||||
|
||||
(defn- increment-hash-values
|
||||
"Potenially adds a new value to the list of input seq based on increment. Used to 'increment' a hash value to avoid duplicates."
|
||||
"Potentially adds a new value to the list of input seq based on increment. Used to 'increment' a hash value to avoid duplicates."
|
||||
[values increment]
|
||||
(if (= increment 0)
|
||||
values
|
||||
@@ -418,7 +418,7 @@
|
||||
- `:skip`: a vector of field names, used it tests to check if all fields were specified (`:id` and `:updated_at`
|
||||
are always skipped, no need to mention them).
|
||||
- `:transform`: is a map like `{:field-name {:export (fn [v] ...) :import (fn [v] ...)}}`. For behavior see docs
|
||||
on `extract-one` and `xform-one`. There are a number of transfomers, see this field for `fk` and similar.
|
||||
on `extract-one` and `xform-one`. There are a number of transformers, see this field for `fk` and similar.
|
||||
- `:coerce`: a map like `{:field-name Schema}`; incoming data will be coerced to schema after `:import`/`:copy`.
|
||||
|
||||
Example (search codebase for more examples):
|
||||
@@ -633,7 +633,7 @@
|
||||
;;;
|
||||
;;; - `(ingest-one serdes-path opts)` is called to read the value into memory, then
|
||||
;;; - `(dependencies ingested)` gets a list of other `:serdes/meta` paths need to be loaded first.
|
||||
;;; - See below on depenencies.
|
||||
;;; - See below on dependencies.
|
||||
;;; - Dependencies are loaded recursively in postorder; that is an entity is loaded after all its deps.
|
||||
;;; - Circular dependencies will make the load process throw.
|
||||
;;; - Once an entity's deps are all loaded, we check for an existing one:
|
||||
@@ -1182,7 +1182,7 @@
|
||||
(if (= db-id lib.schema.id/saved-questions-virtual-database-id)
|
||||
"database/__virtual"
|
||||
(t2/select-one-fn :name :model/Database :id db-id)))
|
||||
(:card_id :card-id) #(*export-fk* % :model/Card) ; attibutes that refer to db fields use `_`; template-tags use `-`
|
||||
(:card_id :card-id) #(*export-fk* % :model/Card) ; attributes that refer to db fields use `_`; template-tags use `-`
|
||||
(:source_table :source-table) export-source-table
|
||||
::mb.viz/param-mapping-source *export-field-fk*
|
||||
:segment #(*export-fk* % :model/Segment)
|
||||
@@ -1419,7 +1419,7 @@
|
||||
|
||||
(defn- json-mbql-fully-qualified-names->ids
|
||||
"Converts fully qualified names to IDs in MBQL embedded inside a JSON string.
|
||||
Returns a new JSON string with teh IDs converted inside."
|
||||
Returns a new JSON string with the IDs converted inside."
|
||||
[json-str]
|
||||
(-> json-str
|
||||
json/decode+kw
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
|
||||
(def ^:dynamic *enable-reverse-joins*
|
||||
"Whether to chain filter via joins where we must follow relationships in reverse, e.g. child -> parent (e.g.
|
||||
Restaurant -> Category instead of the usual Category -> Restuarant*)
|
||||
Restaurant -> Category instead of the usual Category -> Restaurant*)
|
||||
|
||||
This switch mostly exists because I'm not 100% sure what the right behavior is."
|
||||
true)
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
(:id field) constraints {})
|
||||
;; we have a hard limit for how many values we want to store in FieldValues,
|
||||
;; let's make sure we respect that limit here.
|
||||
;; For a more detailed docs on this limt check out [[field-values/distinct-values]]
|
||||
;; For a more detailed docs on this limit check out [[field-values/distinct-values]]
|
||||
limited-values (field-values/take-by-length field-values/*total-max-length* values)]
|
||||
{:values limited-values
|
||||
:has_more_values (or (> (count values)
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
;;; +----------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
(defn assert-valid-parameters
|
||||
"Receive a Paremeterized Object and check if its parameters is valid."
|
||||
"Receive a Parameterized Object and check if its parameters is valid."
|
||||
[{:keys [parameters]}]
|
||||
(let [schema [:maybe ::parameters.schema/parameters]]
|
||||
(when-let [error (mr/explain schema parameters)]
|
||||
@@ -42,7 +42,7 @@
|
||||
:errors (:errors error)})))))
|
||||
|
||||
(defn assert-valid-parameter-mappings
|
||||
"Receive a Paremeterized Object and check if its parameters is valid."
|
||||
"Receive a Parameterized Object and check if its parameters is valid."
|
||||
[{parameter-mappings :parameter_mappings}]
|
||||
(let [schema [:maybe [:sequential ::parameters.schema/parameter-mapping]]]
|
||||
(when-not (mr/validate schema parameter-mappings)
|
||||
@@ -223,7 +223,7 @@
|
||||
ctx)))
|
||||
|
||||
(def ^:dynamic *field-id-context*
|
||||
"Conext for effective computation of field ids for parameters. Bound in
|
||||
"Context for effective computation of field ids for parameters. Bound in
|
||||
the [[metabase.dashboards-rest.api/hydrate-dashboard-details]]. Meant to be used in the [[field-id-into-context-rf]], to
|
||||
re-use values of previous `filterable-columns` computations (during the reduction itself and hydration of
|
||||
`:param_fields` and `:param_values` at the time of writing)."
|
||||
@@ -319,7 +319,7 @@
|
||||
(into #{} cat (vals param-id->field-ids))))
|
||||
|
||||
(defn get-linked-field-ids
|
||||
"Retrieve a map relating paramater ids to field ids."
|
||||
"Retrieve a map relating parameter ids to field ids."
|
||||
[dashcards]
|
||||
(letfn [(targets [{params :parameter_mappings card :card, :as _dashcard}]
|
||||
(into {}
|
||||
|
||||
@@ -224,7 +224,7 @@
|
||||
#"\{\{\s*([A-Za-z0-9_\.]+?)\s*\}\}")
|
||||
|
||||
(def ^:private template-tag-splitting-regex
|
||||
"A regex for spliting text around template tags. This should be identical to `template-tag-regex` above, but without
|
||||
"A regex for splitting text around template tags. This should be identical to `template-tag-regex` above, but without
|
||||
the capture group around the tag name."
|
||||
#"\{\{\s*[A-Za-z0-9_\.]+?\s*\}\}")
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
:having [(perm-condition perm-type required-level (or most-or-least :least))]}])
|
||||
|
||||
(def UserInfo
|
||||
"The user-id to use in the visiblity query and their superuser status."
|
||||
"The user-id to use in the visibility query and their superuser status."
|
||||
[:map
|
||||
[:user-id pos-int?]
|
||||
[:is-superuser? :boolean]])
|
||||
|
||||
@@ -262,7 +262,7 @@
|
||||
this :- [:map
|
||||
[:collection_id [:maybe ms/PositiveInt]]]
|
||||
read-or-write :- [:enum :read :write]]
|
||||
;; based on value of read-or-write determine the approprite function used to calculate the perms path
|
||||
;; based on value of read-or-write determine the appropriate function used to calculate the perms path
|
||||
(let [path-fn (case read-or-write
|
||||
:read permissions.path/collection-read-path
|
||||
:write permissions.path/collection-readwrite-path)
|
||||
@@ -358,10 +358,10 @@
|
||||
"Delete all 'related' permissions for `group-or-id` (i.e., perms that grant you full or partial access to `path`).
|
||||
This includes *both* ancestor and descendant paths. For example:
|
||||
|
||||
Suppose we asked this functions to delete related permssions for `/db/1/schema/PUBLIC/`. Depending on the
|
||||
Suppose we asked this functions to delete related permissions for `/db/1/schema/PUBLIC/`. Depending on the
|
||||
permissions the group has, it could end up doing something like:
|
||||
|
||||
* deleting `/db/1/` permissions (because the ancestor perms implicity grant you full perms for `schema/PUBLIC`)
|
||||
* deleting `/db/1/` permissions (because the ancestor perms implicitly grant you full perms for `schema/PUBLIC`)
|
||||
* deleting perms for `/db/1/schema/PUBLIC/table/2/` (because Table 2 is a descendant of `schema/PUBLIC`)
|
||||
|
||||
In short, it will delete any permissions that contain `/db/1/schema/` as a prefix, or that themeselves are prefixes
|
||||
@@ -437,12 +437,12 @@
|
||||
; Audit permissions helper fns end
|
||||
|
||||
(defn revoke-application-permissions!
|
||||
"Remove all permissions entries for a Group to access a Application permisisons"
|
||||
"Remove all permissions entries for a Group to access a Application permissions"
|
||||
[group-or-id perm-type]
|
||||
(delete-related-permissions! group-or-id (permissions.path/application-perms-path perm-type)))
|
||||
|
||||
(defn grant-application-permissions!
|
||||
"Grant full permissions for a group to access a Application permisisons."
|
||||
"Grant full permissions for a group to access a Application permissions."
|
||||
[group-or-id perm-type]
|
||||
(when (and (perms-group/is-tenant-group? group-or-id)
|
||||
(not= perm-type :subscription))
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
;; IMPORTANT: If you add a new permission type, `:values` must be ordered from *most* permissive to *least* permissive.
|
||||
;;
|
||||
;; - When fetching a user's permissions, the default behavior is to return the *most* permissive value from any group the
|
||||
;; user is in. This can be overridden by definding a custom implementation of `coalesce`.
|
||||
;; user is in. This can be overridden by defining a custom implementation of `coalesce`.
|
||||
;;
|
||||
;; - If a user does not have any value for the permission when it is fetched, the *least* permissive value is used as a
|
||||
;; fallback.
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
"Any path starting with /application is a permissions that is not scoped by database or collection
|
||||
/application/setting/ -> permissions to access /admin/settings page
|
||||
/application/monitoring/ -> permissions to access tools, audit and troubleshooting
|
||||
/application/subscription/ -> permisisons to create/edit subscriptions and alerts"
|
||||
/application/subscription/ -> permissions to create/edit subscriptions and alerts"
|
||||
[:and "application/"
|
||||
[:or "setting/" "monitoring/" "subscription/"]])
|
||||
|
||||
|
||||
@@ -424,7 +424,7 @@
|
||||
:data-model (update-db-level-metadata-permissions! group-id db-id new-perms)
|
||||
:details (update-details-perms! group-id db-id new-perms))))))
|
||||
|
||||
;; The following arity is provided soley for convenience for tests/REPL usage
|
||||
;; The following arity is provided solely for convenience for tests/REPL usage
|
||||
([ks :- [:vector :any] new-value]
|
||||
(update-data-perms-graph!* (assoc-in (-> api-graph :groups) ks new-value))))
|
||||
|
||||
@@ -441,6 +441,6 @@
|
||||
(delete-impersonations-if-needed-after-permissions-change! group-updates)
|
||||
(delete-gtaps-if-needed-after-permissions-change! group-updates))))))
|
||||
|
||||
;; The following arity is provided soley for convenience for tests/REPL usage
|
||||
;; The following arity is provided solely for convenience for tests/REPL usage
|
||||
([ks :- [:vector :any] new-value]
|
||||
(update-data-perms-graph! (assoc-in (api-graph) (cons :groups ks) new-value))))
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
(js->clj (js/JSON.parse x))))
|
||||
|
||||
(defn- ensure-consistent-type
|
||||
"Convert Clojure value that may have ambigous type into canonical type to ensure it can be used as a key.
|
||||
"Convert Clojure value that may have ambiguous type into canonical type to ensure it can be used as a key.
|
||||
Does nothing in CLJS."
|
||||
[x]
|
||||
#?(:cljs x
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(ns metabase.plugins.init-steps
|
||||
"Logic for performing the `init-steps` listed in a Metabase plugin's manifest. For driver plugins that specify that we
|
||||
should `lazy-load`, these steps are lazily performed the first time non-trivial driver methods (such as connecting
|
||||
to a Database) are called; for all other Metabase plugins these are perfomed during launch.
|
||||
to a Database) are called; for all other Metabase plugins these are performed during launch.
|
||||
|
||||
The entire list of possible init steps is below, as impls for the `do-init-step!` multimethod."
|
||||
(:require
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user