(ns metabase.query-processor.middleware.resolve-referenced (:require [metabase.lib.core :as lib] [metabase.lib.metadata :as lib.metadata] [metabase.lib.schema :as lib.schema] [metabase.lib.schema.common :as lib.schema.common] [metabase.lib.schema.id :as lib.schema.id] [metabase.lib.schema.metadata :as lib.schema.metadata] [metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.middleware.fetch-source-query :as qp.fetch-source-query] [metabase.query-processor.middleware.resolve-fields :as qp.resolve-fields] [metabase.query-processor.middleware.resolve-source-table :as qp.resolve-source-table] [metabase.util.i18n :refer [tru]] [metabase.util.malli :as mu] [weavejester.dependency :as dep]) (:import (clojure.lang ExceptionInfo))) | |
Done for side effects; warm the MetadataProvider. | (mu/defn- resolve-referenced-card-resources* [query :- ::lib.schema/query] (doseq [referenced-card (lib/template-tags-referenced-cards query) :let [referenced-query (->> referenced-card (qp.fetch-source-query/normalize-card-query query) :dataset-query) resolved-query (qp.fetch-source-query/resolve-source-cards referenced-query)]] (qp.resolve-source-table/resolve-source-tables resolved-query) (qp.resolve-fields/resolve-fields resolved-query))) |
(mu/defn- card-subquery-graph [metadata-providerable :- ::lib.schema.metadata/metadata-providerable graph :- :map card-id :- ::lib.schema.id/card] (let [card-query (->> (or (lib.metadata/card metadata-providerable card-id) (throw (ex-info (tru "Card {0} does not exist, or is from a different Database." (pr-str card-id)) {:type qp.error-type/invalid-query, :card-id card-id}))) (qp.fetch-source-query/normalize-card-query metadata-providerable) :dataset-query)] (reduce (fn [g sub-card-id] (card-subquery-graph metadata-providerable (dep/depend g card-id sub-card-id) sub-card-id)) graph (lib/template-tag-card-ids card-query)))) | |
(mu/defn- circular-ref-error :- ::lib.schema.common/non-blank-string [metadata-providerable :- ::lib.schema.metadata/metadata-providerable from-card :- ::lib.schema.id/card to-card :- ::lib.schema.id/card] (let [cards (into {} (map (juxt :id :name)) (lib.metadata/bulk-metadata metadata-providerable :metadata/card #{from-card to-card})) from-name (or (get cards from-card) (throw (ex-info (tru "Referenced query is from a different database") {:type qp.error-type/invalid-query, :card-id from-card}))) to-name (or (get cards to-card) (throw (ex-info (tru "Referenced query is from a different database") {:type qp.error-type/invalid-query, :card-id to-card})))] (str (tru "This query has circular referencing sub-queries. ") (tru "These questions seem to be part of the problem: \"{0}\" and \"{1}\"." from-name to-name)))) | |
Done for side effects; [[card-subquery-graph]] will throw if there are circular references. | (mu/defn- check-for-circular-references [query :- ::lib.schema/query] (try (reduce (partial card-subquery-graph query) (dep/graph) (lib/template-tag-card-ids query)) (catch ExceptionInfo e (let [{:keys [reason node dependency]} (ex-data e)] (if (= reason :weavejester.dependency/circular-dependency) (throw (ex-info (circular-ref-error query node dependency) {:original-exception e})) (throw e)))))) |
(mu/defn resolve-referenced-card-resources :- ::lib.schema/query "Resolves tables and fields referenced in card query template tags." [query :- ::lib.schema/query] (check-for-circular-references query) (resolve-referenced-card-resources* query) query) | |