Primary entrypoints to running Metabase (MBQL) queries. (metabase.query-processor/process-query {:type :query, :database 1, :query {:source-table 2}}) Various REST API endpoints, such as | (ns metabase.query-processor (:require [metabase.lib.schema.info :as lib.schema.info] [metabase.query-processor.compile :as qp.compile] [metabase.query-processor.debug :as qp.debug] [metabase.query-processor.execute :as qp.execute] [metabase.query-processor.middleware.catch-exceptions :as qp.catch-exceptions] [metabase.query-processor.middleware.enterprise :as qp.middleware.enterprise] [metabase.query-processor.middleware.process-userland-query :as qp.process-userland-query] [metabase.query-processor.postprocess :as qp.postprocess] [metabase.query-processor.preprocess :as qp.preprocess] [metabase.query-processor.reducible :as qp.reducible] [metabase.query-processor.schema :as qp.schema] [metabase.query-processor.setup :as qp.setup] [metabase.util.log :as log] [metabase.util.malli :as mu])) |
Middleware that goes AROUND [[process-query]]. Does extra stuff like handling (f qp) -> qp Where (f query rff) | (def around-middleware ;; think of the direction stuff happens in as if you were throwing a ball up in the air; as the query-ball goes up the ;; around middleware pre-processing stuff happens; then the query is executed, as the "ball of results" comes back ;; down any post-processing these around middlewares might do happens in reversed order. ;; ;; ↓↓↓ POST-PROCESSING ↓↓↓ happens from TOP TO BOTTOM [#'qp.middleware.enterprise/handle-audit-app-internal-queries-middleware #'qp.process-userland-query/process-userland-query-middleware ;; userland queries only: catch Exceptions and return a special error response #'qp.catch-exceptions/catch-exceptions]) |
↑↑↑ PRE-PROCESSING ↑↑↑ happens from BOTTOM TO TOP | |
(defn- process-query** [query rff] (qp.debug/debug> (list `process-query query)) (let [preprocessed (qp.preprocess/preprocess query) compiled (qp.compile/attach-compiled-query preprocessed) rff (qp.postprocess/post-processing-rff preprocessed rff)] (qp.execute/execute compiled rff))) | |
(def ^:private ^{:arglists '([query rff])} process-query* nil) | |
(defn- rebuild-process-query-fn! [] (alter-var-root #'process-query* (constantly (reduce (fn [qp middleware] (if middleware (middleware qp) qp)) process-query** around-middleware)))) | |
(rebuild-process-query-fn!) | |
(doseq [varr around-middleware :when varr] (add-watch varr ::reload (fn [_key _ref _old-state _new-state] (log/infof "%s changed, rebuilding %s" varr `process-query*) (rebuild-process-query-fn!)))) | |
(mu/defn process-query :- [:fn {:error/message "process-query unexpectedly returned nil."} some?] "Process an MBQL query. This is the main entrypoint to the magical realm of the Query Processor." ([query] (process-query query nil)) ([query :- ::qp.schema/query rff :- [:maybe ::qp.schema/rff]] (qp.setup/with-qp-setup [query query] (let [rff (or rff qp.reducible/default-rff)] (process-query* query rff))))) | |
(mu/defn userland-query :- ::qp.schema/query "Add middleware options and `:info` to a `query` so it is ran as a 'userland' query, which slightly changes the QP behavior: 1. Exceptions are caught, and a special error shape is returned (see [[catch-exceptions/catch-exceptions]]) 2. A `QueryExecution` is saved in the application database (see [[process-userland-query/process-userland-query-middleware]]) 3. A few extra keys like `:running_time` and `:started_at` are added to the QP response (see [[process-userland-query/process-userland-query-middleware]])" ([query] (userland-query query nil)) ([query :- ::qp.schema/query info :- [:maybe ::lib.schema.info/info]] (-> query (assoc-in [:middleware :userland-query?] true) (update :info merge info)))) | |
(mu/defn userland-query-with-default-constraints :- ::qp.schema/query "Add middleware options and `:info` to a `query` so it is ran as a 'userland' query. QP behavior changes are the same as those for [[userland-query]], *plus* the default userland constraints (limits) are applied -- see [[qp.constraints/add-default-userland-constraints]]. This ultimately powers most of the REST API entrypoints into the QP." ([query] (userland-query-with-default-constraints query nil)) ([query :- ::qp.schema/query info :- [:maybe ::lib.schema.info/info]] (-> query (userland-query info) (assoc-in [:middleware :add-default-userland-constraints?] true)))) | |