diff --git a/src/metabase/query_analysis.clj b/src/metabase/query_analysis.clj index d09e578f012d5ef7cc434a747036624712203404..09fa1a72f9ff2cda269d968e0750024f02d39d9d 100644 --- a/src/metabase/query_analysis.clj +++ b/src/metabase/query_analysis.clj @@ -108,7 +108,8 @@ :native (try (nqa/references-for-native query) (catch Exception e - (log/debug e "Failed to analyze native query" query))) + (log/debug e "Failed to analyze native query" query) + {:error :query-analysis.error/exception, :context {:exception e}})) ;; For now, all model references are resolved transitively to the ultimate field ids. ;; We may want to change to record model references directly rather than resolving them. ;; This would remove the need to invalidate consuming cards when a given model changes. @@ -124,36 +125,38 @@ (let [query-type (lib/normalized-query-type query)] (when (enabled-type? query-type) (t2/with-transaction [_conn] - (let [analysis-id (t2/insert-returning-pk! :model/QueryAnalysis {:card_id card-id :status "running"}) - references (query-references query query-type) - table->row (fn [{:keys [schema table table-id]}] - {:card_id card-id - :analysis_id analysis-id - :schema schema - :table table - :table_id table-id}) - field->row (fn [{:keys [schema table column table-id field-id explicit-reference]}] - {:card_id card-id - :analysis_id analysis-id - :schema schema - :table table - :column column - :table_id table-id - :field_id field-id - :explicit_reference explicit-reference}) - success? (some? references)] - - (if-not success? + (let [analysis-id (t2/insert-returning-pk! :model/QueryAnalysis {:card_id card-id :status "running"}) + result (query-references query query-type) + table->row (fn [{:keys [schema table table-id]}] + {:card_id card-id + :analysis_id analysis-id + :schema schema + :table table + :table_id table-id}) + field->row (fn [{:keys [schema table column table-id field-id explicit-reference]}] + {:card_id card-id + :analysis_id analysis-id + :schema schema + :table table + :column column + :table_id table-id + :field_id field-id + :explicit_reference explicit-reference})] + + (if (contains? result :error) + ;; TODO we should track cases where the driver is disabled or not-supported differently. (t2/update! :model/QueryAnalysis analysis-id {:status "failed"}) (do - (t2/insert! :model/QueryField (map field->row (:fields references))) - (t2/insert! :model/QueryTable (map table->row (:tables references))) + (t2/insert! :model/QueryField (map field->row (:fields result))) + (t2/insert! :model/QueryTable (map table->row (:tables result))) (t2/update! :model/QueryAnalysis analysis-id {:status "complete"}))) (t2/delete! :model/QueryAnalysis {:where [:and [:= :card_id card-id] - [:not= :id analysis-id]]})))))) + [:not= :id analysis-id]]}) + + result))))) (defn- replaced-inner-query-for-native-card "Substitute new references for certain fields and tables, based upon the given mappings." diff --git a/src/metabase/query_analysis/native_query_analyzer.clj b/src/metabase/query_analysis/native_query_analyzer.clj index 975e746cbbd4a0c95f82fa145d51d331b578feae..daadac31156250bef3545c91e4773677d01e164c 100644 --- a/src/metabase/query_analysis/native_query_analyzer.clj +++ b/src/metabase/query_analysis/native_query_analyzer.clj @@ -335,8 +335,9 @@ ;; For now we are not restricting this as we are for [[tables-for-native]], yet. ;; As we don't have any hard dependencies on this data, its useful to gather info on which drivers have errors or ;; inaccuracies in practice. - (when (isa? driver/hierarchy driver :sql) - (references-for-sql driver query)))) + (if (isa? driver/hierarchy driver :sql) + (references-for-sql driver query) + {:error :query-analysis.error/driver-not-supported}))) (defn tables-for-native "Returns a set of table identifiers that (may) be referenced in the given card's query. diff --git a/src/metabase/task/analyze_queries.clj b/src/metabase/task/analyze_queries.clj index 222f849f3525d46f419c77d4861e2c491efc25be..6a8d41eb97cadac933c94370af6fc92fec09a2ca 100644 --- a/src/metabase/task/analyze_queries.clj +++ b/src/metabase/task/analyze_queries.clj @@ -48,8 +48,9 @@ (if (failure-map/non-retryable? card) (log/warnf "Skipping analysis of Card %s as its query has caused failures in the past." card-id) (try - (query-analysis/analyze!* card) - (failure-map/track-success! card) + (if (:error (query-analysis/analyze!* card)) + (failure-map/track-failure! card) + (failure-map/track-success! card)) (let [taken-ms (Math/ceil (u/since-ms timer)) sleep-ms (wait-proportional taken-ms)] (log/debugf "Query analysis for Card %s took %sms (incl. persisting)" card-id taken-ms) diff --git a/test/metabase/query_analysis/native_query_analyzer_test.clj b/test/metabase/query_analysis/native_query_analyzer_test.clj index 671b35e827f1e359b9f4d9e2f0efd7212a99bbd3..b0083d88fee30d534cf9e1e38cb01257d1509fd1 100644 --- a/test/metabase/query_analysis/native_query_analyzer_test.clj +++ b/test/metabase/query_analysis/native_query_analyzer_test.clj @@ -41,8 +41,10 @@ (defn- refs [sql] (-> (mt/native-query {:query sql}) (#'nqa/references-for-native) + (select-keys [:fields :tables]) (update-vals - (partial sort-by (juxt :schema :table :column))))) + (partial sort-by (juxt :schema :table :column))) + not-empty)) (defn- field-refs [sql] (:fields (refs sql))) diff --git a/test/metabase/task/setup/query_analysis_setup.clj b/test/metabase/task/setup/query_analysis_setup.clj index a415efa7f8c3d852e23240514eec3fb4367b1f90..cc3d5506a42e92bdaf9578ac8b8926865288d891 100644 --- a/test/metabase/task/setup/query_analysis_setup.clj +++ b/test/metabase/task/setup/query_analysis_setup.clj @@ -17,34 +17,35 @@ mlv2-query (-> (lib/query metadata-provider venues) (lib/aggregate (lib/distinct venues-name)))] - (mt/with-temp [Card c1 {:query_type "native" - :dataset_query (mt/native-query {:query "SELECT id FROM venues"})} - Card c2 {:query_type "native" - :dataset_query (mt/native-query {:query "SELECT id FROM venues WHERE name = {{ name }}" - :template-tags {"name" {:id "_name_" - :type :text - :display-name "name" - :default "qwe"}}})} - Card c3 {:query_type "query" - :dataset_query (mt/mbql-query venues {:aggregation [[:distinct $name]]})} - Card c4 {:query_type "query" - :dataset_query mlv2-query} - Card archived {:archived true - :query_type "native" - :dataset_query (mt/native-query {:query "SELECT id FROM venues"})} - Card invalid {:query_type "native" - :dataset_query (mt/native-query {:query "SELECT boom, FROM"})}] + (mt/with-temporary-setting-values [query-analysis-enabled true] + (mt/with-temp [Card c1 {:query_type "native" + :dataset_query (mt/native-query {:query "SELECT id FROM venues"})} + Card c2 {:query_type "native" + :dataset_query (mt/native-query {:query "SELECT id FROM venues WHERE name = {{ name }}" + :template-tags {"name" {:id "_name_" + :type :text + :display-name "name" + :default "qwe"}}})} + Card c3 {:query_type "query" + :dataset_query (mt/mbql-query venues {:aggregation [[:distinct $name]]})} + Card c4 {:query_type "query" + :dataset_query mlv2-query} + Card archived {:archived true + :query_type "native" + :dataset_query (mt/native-query {:query "SELECT id FROM venues"})} + Card invalid {:query_type "native" + :dataset_query (mt/native-query {:query "SELECT boom, FROM"})}] - ;; Make sure there is no existing analysis for the relevant cards - (t2/delete! :model/QueryAnalysis :card_id [:in (map :id [c1 c2 c3 c4 archived invalid])]) + ;; Make sure there is no existing analysis for the relevant cards + (t2/delete! :model/QueryAnalysis :card_id [:in (map :id [c1 c2 c3 c4 archived invalid])]) - ;; Make sure some other card has analysis - (query-analysis/analyze!* c3) + ;; Make sure some other card has analysis + (query-analysis/analyze!* c3) - ;; And attempt to analyze an invalid query - (query-analysis/analyze!* invalid) + ;; And attempt to analyze an invalid query + (query-analysis/analyze!* invalid) - (mt/call-with-map-params f [c1 c2 c3 c4 archived invalid])))) + (mt/call-with-map-params f [c1 c2 c3 c4 archived invalid]))))) (defmacro with-test-setup! "Set up the data required to test the Query Analysis related tasks"