diff --git a/src/metabase/driver.clj b/src/metabase/driver.clj index 96b55cdf6be7fe043724cee6416b0ed7fcce18ca..b2a9010c1287a1d18f838a7f4fbcbc268c9f6363 100644 --- a/src/metabase/driver.clj +++ b/src/metabase/driver.clj @@ -1,5 +1,5 @@ (ns metabase.driver - (:require clojure.java.classpath + (:require [clojure.java.classpath :as classpath] [clojure.string :as s] [clojure.tools.logging :as log] [clojure.tools.namespace.find :as ns-find] @@ -13,26 +13,269 @@ (declare -dataset-query query-fail query-complete save-query-execution) +;;; ## INTERFACE + CONSTANTS + +(def ^:const max-sync-lazy-seq-results + "The maximum number of values we should return when using `field-values-lazy-seq`. + This many is probably fine for inferring special types and what-not; we don't want + to scan millions of values at any rate." + 10000) + +(def ^:const connection-error-messages + "Generic error messages that drivers should return in their implementation of `humanize-connection-error-message`." + {:cannot-connect-check-host-and-port "Hmm, we couldn't connect to the database. Make sure your host and port settings are correct." + :database-name-incorrect "Looks like the database name is incorrect." + :invalid-hostname "It looks like your host is invalid. Please double-check it and try again." + :password-incorrect "Looks like your password is incorrect." + :password-required "Looks like you forgot to enter your password." + :username-incorrect "Looks like your username is incorrect." + :username-or-password-incorrect "Looks like the username or password is incorrect."}) + +(def ^:private ^:const feature->required-fns + "Map of optional driver features (as keywords) to a set of functions drivers that support that feature must define." + {:foreign-keys #{:table-fks} + :nested-fields #{:active-nested-field-name->type} + :set-timezone nil + :standard-deviation-aggregations nil + :unix-timestamp-special-type-fields nil}) + +(def ^:private ^:const optional-features + (set (keys feature->required-fns))) + +(def ^:private ^:const required-fns + #{:can-connect? + :active-table-names + :active-column-names->type + :table-pks + :field-values-lazy-seq + :process-query}) + +(def ^:private ^:const optional-fns + #{:humanize-connection-error-message + :sync-in-context + :process-query-in-context + :table-rows-seq + :field-avg-length + :field-percent-urls + :driver-specific-sync-field!}) + +(defn verify-driver + "Verify that a Metabase DB driver contains the expected properties and that they are the correct type." + [{:keys [driver-name details-fields features], :as driver}] + ;; Check :driver-name is a string + (assert driver-name + "Missing property :driver-name.") + (assert (string? driver-name) + ":driver-name must be a string.") + + ;; Check the :details-fields + (assert details-fields + "Driver is missing property :details-fields.") + (assert (vector? details-fields) + ":details-fields should be a vector.") + (doseq [f details-fields] + (assert (map? f) + (format "Details fields must be maps: %s" f)) + (assert (:name f) + (format "Details field %s is missing a :name property." f)) + (assert (:display-name f) + (format "Details field %s is missing a :display-name property." f)) + (when (:type f) + (assert (contains? #{:string :integer :password :boolean} (:type f)) + (format "Invalid type %s in details field %s." (:type f) f))) + (when (:default f) + (assert (not (:placeholder f)) + (format "Fields should not define both :default and :placeholder: %s" f)) + (assert (not (:required f)) + (format "Fields that define a :default cannot be :required: %s" f)))) + + ;; Check that all required functions are defined + (doseq [f required-fns] + (assert (f driver) + (format "Missing fn: %s" f)) + (assert (fn? (f driver)) + (format "Not a fn: %s" (f driver)))) + + ;; Check that all features declared are valid + (when features + (assert (and (set? features) + (every? keyword? features)) + ":features must be a set of keywords.") + (doseq [feature features] + (assert (contains? optional-features feature) + (format "Not a valid feature: %s" feature)) + (doseq [f (feature->required-fns feature)] + (assert (f driver) + (format "Drivers that support feature %s must have fn %s." feature f)) + (assert (fn? (f driver)) + (format "Not a fn: %s" f))))) + + ;; Check that the optional fns, if included, are actually fns + (doseq [f optional-fns] + (when (f driver) + (assert (fn? (f driver)) + (format "Not a fn: %s" f))))) + +(defmacro defdriver + "Define and validate a new Metabase DB driver. + + All drivers must include the following keys: + +#### PROPERTIES + +* `:driver-name` + + A human-readable string naming the DB this driver works with, e.g. `\"PostgreSQL\"`. + +* `:details-fields` + + A vector of maps that contain information about connection properties that should + be exposed to the user for databases that will use this driver. This information is used to build the UI for editing + a `Database` `details` map, and for validating it on the Backend. It should include things like `host`, + `port`, and other driver-specific parameters. Each field information map should have the following properties: + + * `:name` + + The key that should be used to store this property in the `details` map. + + * `:display-name` + + Human-readable name that should be displayed to the User in UI for editing this field. + + * `:type` *(OPTIONAL)* + + `:string`, `:integer`, `:boolean`, or `:password`. Defaults to `:string`. + + * `:default` *(OPTIONAL)* + + A default value for this field if the user hasn't set an explicit value. This is shown in the UI as a placeholder. + + * `:placeholder` *(OPTIONAL)* + + Placeholder value to show in the UI if user hasn't set an explicit value. Similar to `:default`, but this value is + *not* saved to `:details` if no explicit value is set. Since `:default` values are also shown as placeholders, you + cannot specify both `:default` and `:placeholder`. + + * `:required` *(OPTIONAL)* + + Is this property required? Defaults to `false`. + +* `:features` *(OPTIONAL)* + + A set of keyword names of optional features supported by this driver, such as `:foreign-keys`. + +#### FUNCTIONS + +* `(can-connect? [details-map])` + + Check whether we can connect to a `Database` with DETAILS-MAP and perform a simple query. For example, a SQL database might + try running a query like `SELECT 1;`. This function should return `true` or `false`. + +* `(active-table-names [database])` + + Return a set of string names of tables, collections, or equivalent that currently exist in DATABASE. + +* `(active-column-names->type [table])` + + Return a map of string names of active columns (or equivalent) -> `Field` `base_type` for TABLE (or equivalent). + +* `(table-pks [table])` + + Return a set of string names of active Fields that are primary keys for TABLE (or equivalent). + +* `(field-values-lazy-seq [field])` + + Return a lazy sequence of all values of FIELD. + This is used to implement `mark-json-field!`, and fallback implentations of `mark-no-preview-display-field!` and `mark-url-field!` + if drivers *don't* implement `field-avg-length` and `field-percent-urls`, respectively. + +* `(process-query [query])` + + Process a native or structured QUERY. This function is called by `metabase.driver/process-query` after performing various driver-unspecific + steps like Query Expansion and other preprocessing. + +* `(table-fks [table])` *(REQUIRED FOR DRIVERS THAT SUPPORT `:foreign-keys`)* + + Return a set of maps containing info about FK columns for TABLE. + Each map should contain the following keys: + + * `fk-column-name` + * `dest-table-name` + * `dest-column-name` + +* `(active-nested-field-name->type [field])` *(REQUIRED FOR DRIVERS THAT SUPPORT `:nested-fields`)* + + Return a map of string names of active child `Fields` of FIELD -> `Field.base_type`. + +* `(humanize-connection-error-message [message])` *(OPTIONAL)* + + Return a humanized (user-facing) version of an connection error message string. + Generic error messages are provided in the constant `connection-error-messages`; return one of these whenever possible. + +* `(sync-in-context [database f])` *(OPTIONAL)* + + Drivers may provide this function if they need to do special setup before a sync operation such as `sync-database!`. The sync + operation itself is encapsulated as the lambda F, which must be called with no arguments. + + (defn sync-in-context [database f] + (with-jdbc-metadata [_ database] + (f))) + +* `(process-query-in-context [f])` *(OPTIONAL)* + + Similar to `sync-in-context`, but for running queries rather than syncing. This should be used to do things like open DB connections + that need to remain open for the duration of post-processing. This function follows a middleware pattern and is injected into the QP + middleware stack immediately after the Query Expander; in other words, it will receive the expanded query. + See the Mongo and H2 drivers for examples of how this is intended to be used. + +* `(table-rows-seq [database table-name])` *(OPTIONAL)* + + Return a sequence of all the rows in a table with a given TABLE-NAME. + Currently, this is only used for iterating over the values in a `_metabase_metadata` table. As such, the results are not expected to be returned lazily. + +* `(field-avg-length [field])` *(OPTIONAL)* + + If possible, provide an efficent DB-level function to calculate the average length of non-nil values of textual FIELD, which is used to determine whether a `Field` + should be marked as a `:category`. If this function is not provided, a fallback implementation that iterates over results in Clojure-land is used instead. + +* `(field-percent-urls [field])` *(OPTIONAL)* + + If possible, provide an efficent DB-level function to calculate what percentage of non-nil values of textual FIELD are valid URLs, which is used to determine + whether a `Field` should be marked as a `:url`. If this function is not provided, a fallback implementation that iterates over results in Clojure-land is used instead. + +* `(driver-specific-sync-field! [field])` *(OPTIONAL)* + + This is a chance for drivers to do custom `Field` syncing specific to their database. + For example, the Postgres driver can mark Postgres JSON fields as `special_type = json`. + As with the other Field syncing functions in `metabase.driver.sync`, this method should return the modified FIELD, if any, or `nil`." + [driver-name driver-map] + `(def ~(vary-meta driver-name assoc :metabase.driver/driver (keyword driver-name)) + (let [m# ~driver-map] + (verify-driver m#) + m#))) + + ;;; ## CONFIG (defsetting report-timezone "Connection timezone to use when executing queries. Defaults to system timezone.") -;; ## Constants +(defn- -available-drivers [] + (->> (for [namespace (->> (ns-find/find-namespaces (classpath/classpath)) + (filter (fn [ns-symb] + (re-matches #"^metabase\.driver\.[a-z0-9_]+$" (name ns-symb)))))] + (do (require namespace) + (->> (ns-publics namespace) + (map (fn [[symb varr]] + (when (::driver (meta varr)) + {(keyword symb) (select-keys @varr [:details-fields + :driver-name + :features])}))) + (into {})))) + (into {}))) (def available-drivers "Delay to a map of info about available drivers." - (delay (->> (for [namespace (->> (ns-find/find-namespaces (clojure.java.classpath/classpath)) - (filter (fn [ns-symb] - (re-matches #"^metabase\.driver\.[a-z0-9_]+$" (name ns-symb)))))] - (do (require namespace) - (->> (ns-publics namespace) - (map (fn [[symb varr]] - (when (::driver (meta varr)) - {(keyword symb) (select-keys @varr [:details-fields - :driver-name - :features])}))) - (into {})))) - (into {})))) + (delay (-available-drivers))) (defn is-engine? "Is ENGINE a valid driver name?" diff --git a/src/metabase/driver/generic_sql.clj b/src/metabase/driver/generic_sql.clj index ca77f59b92f399535175f890209315883545ac53..4cea8c8a18f9661f5ddc8aeec1eecb28f83918c1 100644 --- a/src/metabase/driver/generic_sql.clj +++ b/src/metabase/driver/generic_sql.clj @@ -3,9 +3,7 @@ [clojure.tools.logging :as log] [korma.core :as k] [korma.sql.utils :as utils] - [metabase.driver :as driver] - (metabase.driver [interface :refer [max-sync-lazy-seq-results defdriver]] - [sync :as driver-sync]) + [metabase.driver :refer [max-sync-lazy-seq-results defdriver]] (metabase.driver.generic-sql [query-processor :as qp] [util :refer :all]) [metabase.models.field :as field] diff --git a/src/metabase/driver/h2.clj b/src/metabase/driver/h2.clj index 7219f041807ae613d02e3ea1c7eae4fb54790186..9a0f0c628055c8c3c85cd6fce0aaa495e064ff36 100644 --- a/src/metabase/driver/h2.clj +++ b/src/metabase/driver/h2.clj @@ -3,8 +3,8 @@ [korma.db :as kdb] [korma.sql.utils :as utils] [metabase.db :as db] - (metabase.driver [generic-sql :refer [sql-driver]] - [interface :as i, :refer [defdriver]]) + [metabase.driver :as driver, :refer [defdriver]] + [metabase.driver.generic-sql :refer [sql-driver]] [metabase.driver.generic-sql.util :refer [funcs]] [metabase.models.database :refer [Database]])) @@ -183,13 +183,13 @@ (defn- humanize-connection-error-message [message] (condp re-matches message #"^A file path that is implicitly relative to the current working directory is not allowed in the database URL .*$" - (i/connection-error-messages :cannot-connect-check-host-and-port) + (driver/connection-error-messages :cannot-connect-check-host-and-port) #"^Database .* not found .*$" - (i/connection-error-messages :cannot-connect-check-host-and-port) + (driver/connection-error-messages :cannot-connect-check-host-and-port) #"^Wrong user name or password .*$" - (i/connection-error-messages :username-or-password-incorrect) + (driver/connection-error-messages :username-or-password-incorrect) #".*" ; default message)) diff --git a/src/metabase/driver/interface.clj b/src/metabase/driver/interface.clj deleted file mode 100644 index 8baea6b53f4f620fcfefedb8a9a70dc5a53124be..0000000000000000000000000000000000000000 --- a/src/metabase/driver/interface.clj +++ /dev/null @@ -1,240 +0,0 @@ -(ns metabase.driver.interface) - -(def ^:const max-sync-lazy-seq-results - "The maximum number of values we should return when using `field-values-lazy-seq`. - This many is probably fine for inferring special types and what-not; we don't want - to scan millions of values at any rate." - 10000) - -(def ^:const connection-error-messages - "Generic error messages that drivers should return in their implementation of `humanize-connection-error-message`." - {:cannot-connect-check-host-and-port "Hmm, we couldn't connect to the database. Make sure your host and port settings are correct." - :database-name-incorrect "Looks like the database name is incorrect." - :invalid-hostname "It looks like your host is invalid. Please double-check it and try again." - :password-incorrect "Looks like your password is incorrect." - :password-required "Looks like you forgot to enter your password." - :username-incorrect "Looks like your username is incorrect." - :username-or-password-incorrect "Looks like the username or password is incorrect."}) - -(def ^:private ^:const feature->required-fns - "Map of optional driver features (as keywords) to a set of functions drivers that support that feature must define." - {:foreign-keys #{:table-fks} - :nested-fields #{:active-nested-field-name->type} - :set-timezone nil - :standard-deviation-aggregations nil - :unix-timestamp-special-type-fields nil}) - -(def ^:private ^:const optional-features - (set (keys feature->required-fns))) - -(def ^:private ^:const required-fns - #{:can-connect? - :active-table-names - :active-column-names->type - :table-pks - :field-values-lazy-seq - :process-query}) - -(def ^:private ^:const optional-fns - #{:humanize-connection-error-message - :sync-in-context - :process-query-in-context - :table-rows-seq - :field-avg-length - :field-percent-urls - :driver-specific-sync-field!}) - -(defn verify-driver - "Verify that a Metabase DB driver contains the expected properties and that they are the correct type." - [{:keys [driver-name details-fields features], :as driver}] - ;; Check :driver-name is a string - (assert driver-name - "Missing property :driver-name.") - (assert (string? driver-name) - ":driver-name must be a string.") - - ;; Check the :details-fields - (assert details-fields - "Driver is missing property :details-fields.") - (assert (vector? details-fields) - ":details-fields should be a vector.") - (doseq [f details-fields] - (assert (map? f) - (format "Details fields must be maps: %s" f)) - (assert (:name f) - (format "Details field %s is missing a :name property." f)) - (assert (:display-name f) - (format "Details field %s is missing a :display-name property." f)) - (when (:type f) - (assert (contains? #{:string :integer :password :boolean} (:type f)) - (format "Invalid type %s in details field %s." (:type f) f))) - (when (:default f) - (assert (not (:placeholder f)) - (format "Fields should not define both :default and :placeholder: %s" f)) - (assert (not (:required f)) - (format "Fields that define a :default cannot be :required: %s" f)))) - - ;; Check that all required functions are defined - (doseq [f required-fns] - (assert (f driver) - (format "Missing fn: %s" f)) - (assert (fn? (f driver)) - (format "Not a fn: %s" (f driver)))) - - ;; Check that all features declared are valid - (when features - (assert (and (set? features) - (every? keyword? features)) - ":features must be a set of keywords.") - (doseq [feature features] - (assert (contains? optional-features feature) - (format "Not a valid feature: %s" feature)) - (doseq [f (feature->required-fns feature)] - (assert (f driver) - (format "Drivers that support feature %s must have fn %s." feature f)) - (assert (fn? (f driver)) - (format "Not a fn: %s" f))))) - - ;; Check that the optional fns, if included, are actually fns - (doseq [f optional-fns] - (when (f driver) - (assert (fn? (f driver)) - (format "Not a fn: %s" f))))) - -(defmacro defdriver - "Define and validate a new Metabase DB driver. - - All drivers must include the following keys: - -#### PROPERTIES - -* `:driver-name` - - A human-readable string naming the DB this driver works with, e.g. `\"PostgreSQL\"`. - -* `:details-fields` - - A vector of maps that contain information about connection properties that should - be exposed to the user for databases that will use this driver. This information is used to build the UI for editing - a `Database` `details` map, and for validating it on the Backend. It should include things like `host`, - `port`, and other driver-specific parameters. Each field information map should have the following properties: - - * `:name` - - The key that should be used to store this property in the `details` map. - - * `:display-name` - - Human-readable name that should be displayed to the User in UI for editing this field. - - * `:type` *(OPTIONAL)* - - `:string`, `:integer`, `:boolean`, or `:password`. Defaults to `:string`. - - * `:default` *(OPTIONAL)* - - A default value for this field if the user hasn't set an explicit value. This is shown in the UI as a placeholder. - - * `:placeholder` *(OPTIONAL)* - - Placeholder value to show in the UI if user hasn't set an explicit value. Similar to `:default`, but this value is - *not* saved to `:details` if no explicit value is set. Since `:default` values are also shown as placeholders, you - cannot specify both `:default` and `:placeholder`. - - * `:required` *(OPTIONAL)* - - Is this property required? Defaults to `false`. - -* `:features` *(OPTIONAL)* - - A set of keyword names of optional features supported by this driver, such as `:foreign-keys`. - -#### FUNCTIONS - -* `(can-connect? [details-map])` - - Check whether we can connect to a `Database` with DETAILS-MAP and perform a simple query. For example, a SQL database might - try running a query like `SELECT 1;`. This function should return `true` or `false`. - -* `(active-table-names [database])` - - Return a set of string names of tables, collections, or equivalent that currently exist in DATABASE. - -* `(active-column-names->type [table])` - - Return a map of string names of active columns (or equivalent) -> `Field` `base_type` for TABLE (or equivalent). - -* `(table-pks [table])` - - Return a set of string names of active Fields that are primary keys for TABLE (or equivalent). - -* `(field-values-lazy-seq [field])` - - Return a lazy sequence of all values of FIELD. - This is used to implement `mark-json-field!`, and fallback implentations of `mark-no-preview-display-field!` and `mark-url-field!` - if drivers *don't* implement `field-avg-length` and `field-percent-urls`, respectively. - -* `(process-query [query])` - - Process a native or structured QUERY. This function is called by `metabase.driver/process-query` after performing various driver-unspecific - steps like Query Expansion and other preprocessing. - -* `(table-fks [table])` *(REQUIRED FOR DRIVERS THAT SUPPORT `:foreign-keys`)* - - Return a set of maps containing info about FK columns for TABLE. - Each map should contain the following keys: - - * `fk-column-name` - * `dest-table-name` - * `dest-column-name` - -* `(active-nested-field-name->type [field])` *(REQUIRED FOR DRIVERS THAT SUPPORT `:nested-fields`)* - - Return a map of string names of active child `Fields` of FIELD -> `Field.base_type`. - -* `(humanize-connection-error-message [message])` *(OPTIONAL)* - - Return a humanized (user-facing) version of an connection error message string. - Generic error messages are provided in the constant `connection-error-messages`; return one of these whenever possible. - -* `(sync-in-context [database f])` *(OPTIONAL)* - - Drivers may provide this function if they need to do special setup before a sync operation such as `sync-database!`. The sync - operation itself is encapsulated as the lambda F, which must be called with no arguments. - - (defn sync-in-context [database f] - (with-jdbc-metadata [_ database] - (f))) - -* `(process-query-in-context [f])` *(OPTIONAL)* - - Similar to `sync-in-context`, but for running queries rather than syncing. This should be used to do things like open DB connections - that need to remain open for the duration of post-processing. This function follows a middleware pattern and is injected into the QP - middleware stack immediately after the Query Expander; in other words, it will receive the expanded query. - See the Mongo and H2 drivers for examples of how this is intended to be used. - -* `(table-rows-seq [database table-name])` *(OPTIONAL)* - - Return a sequence of all the rows in a table with a given TABLE-NAME. - Currently, this is only used for iterating over the values in a `_metabase_metadata` table. As such, the results are not expected to be returned lazily. - -* `(field-avg-length [field])` *(OPTIONAL)* - - If possible, provide an efficent DB-level function to calculate the average length of non-nil values of textual FIELD, which is used to determine whether a `Field` - should be marked as a `:category`. If this function is not provided, a fallback implementation that iterates over results in Clojure-land is used instead. - -* `(field-percent-urls [field])` *(OPTIONAL)* - - If possible, provide an efficent DB-level function to calculate what percentage of non-nil values of textual FIELD are valid URLs, which is used to determine - whether a `Field` should be marked as a `:url`. If this function is not provided, a fallback implementation that iterates over results in Clojure-land is used instead. - -* `(driver-specific-sync-field! [field])` *(OPTIONAL)* - - This is a chance for drivers to do custom `Field` syncing specific to their database. - For example, the Postgres driver can mark Postgres JSON fields as `special_type = json`. - As with the other Field syncing functions in `metabase.driver.sync`, this method should return the modified FIELD, if any, or `nil`." - [driver-name driver-map] - `(def ~(vary-meta driver-name assoc :metabase.driver/driver (keyword driver-name)) - (let [m# ~driver-map] - (verify-driver m#) - m#))) diff --git a/src/metabase/driver/mongo.clj b/src/metabase/driver/mongo.clj index 829090aa9ee1743e4f3eab8dba269245353e1845..dc077d8f56ae57cef9da4dd7e2643e415f3a7a72 100644 --- a/src/metabase/driver/mongo.clj +++ b/src/metabase/driver/mongo.clj @@ -11,8 +11,7 @@ [core :as mg] [db :as mdb] [query :as mq]) - [metabase.driver :as driver] - [metabase.driver.interface :as i, :refer [defdriver]] + [metabase.driver :as driver, :refer [defdriver]] (metabase.driver.mongo [query-processor :as qp] [util :refer [*mongo-connection* with-mongo-connection values->base-type]]) [metabase.util :as u])) @@ -27,7 +26,7 @@ [table] (with-mongo-connection [^com.mongodb.DB conn @(:db table)] (->> (mc/find-maps conn (:name table)) - (take i/max-sync-lazy-seq-results) + (take driver/max-sync-lazy-seq-results) (map keys) (map set) (reduce set/union)))) @@ -53,13 +52,13 @@ (defn- humanize-connection-error-message [message] (condp re-matches message #"^Timed out after \d+ ms while waiting for a server .*$" - (i/connection-error-messages :cannot-connect-check-host-and-port) + (driver/connection-error-messages :cannot-connect-check-host-and-port) #"^host and port should be specified in host:port format$" - (i/connection-error-messages :invalid-hostname) + (driver/connection-error-messages :invalid-hostname) #"^Password can not be null when the authentication mechanism is unspecified$" - (i/connection-error-messages :password-required) + (driver/connection-error-messages :password-required) #".*" ; default message)) @@ -109,7 +108,7 @@ ;; Build a map of nested-field-key -> type -> count ;; TODO - using an atom isn't the *fastest* thing in the world (but is the easiest); consider alternate implementation (let [field->type->count (atom {})] - (doseq [val (take i/max-sync-lazy-seq-results (field-values-lazy-seq field))] + (doseq [val (take driver/max-sync-lazy-seq-results (field-values-lazy-seq field))] (when (map? val) (doseq [[k v] val] (swap! field->type->count update-in [k (type v)] #(if % (inc %) 1))))) diff --git a/src/metabase/driver/mysql.clj b/src/metabase/driver/mysql.clj index fadc19bc5fd837b4eb5cfc97990eec7e4a99b1e5..92bec2a2fc7888c11e1059ac17996c83ee6e40c8 100644 --- a/src/metabase/driver/mysql.clj +++ b/src/metabase/driver/mysql.clj @@ -6,8 +6,8 @@ mysql) (korma.sql [engine :refer [sql-func]] [utils :as utils]) - (metabase.driver [generic-sql :refer [sql-driver]] - [interface :as i, :refer [defdriver]]) + [metabase.driver :as driver, :refer [defdriver]] + [metabase.driver.generic-sql :refer [sql-driver]] [metabase.driver.generic-sql.util :refer [funcs]])) ;;; # Korma 0.4.2 Bug Workaround @@ -129,16 +129,16 @@ (defn- humanize-connection-error-message [message] (condp re-matches message #"^Communications link failure\s+The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.$" - (i/connection-error-messages :cannot-connect-check-host-and-port) + (driver/connection-error-messages :cannot-connect-check-host-and-port) #"^Unknown database .*$" - (i/connection-error-messages :database-name-incorrect) + (driver/connection-error-messages :database-name-incorrect) #"Access denied for user.*$" - (i/connection-error-messages :username-or-password-incorrect) + (driver/connection-error-messages :username-or-password-incorrect) #"Must specify port after ':' in connection string" - (i/connection-error-messages :invalid-hostname) + (driver/connection-error-messages :invalid-hostname) #".*" ; default message)) diff --git a/src/metabase/driver/postgres.clj b/src/metabase/driver/postgres.clj index 66660c8cbcd049f2c30c840a8ccd251393ae2182..f7296de27510db6a88931dfcf509c07d09d8cf48 100644 --- a/src/metabase/driver/postgres.clj +++ b/src/metabase/driver/postgres.clj @@ -9,8 +9,8 @@ [swiss.arrows :refer :all] [metabase.db :refer [upd]] [metabase.models.field :refer [Field]] - (metabase.driver [generic-sql :refer [sql-driver]] - [interface :as i, :refer [defdriver]]) + [metabase.driver :as driver, :refer [defdriver]] + [metabase.driver.generic-sql :refer [sql-driver]] [metabase.driver.generic-sql.util :refer [with-jdbc-metadata]]) ;; This is necessary for when NonValidatingFactory is passed in the sslfactory connection string argument, ;; e.x. when connecting to a Heroku Postgres database from outside of Heroku. @@ -148,19 +148,19 @@ (defn- humanize-connection-error-message [message] (condp re-matches message #"^FATAL: database \".*\" does not exist$" - (i/connection-error-messages :database-name-incorrect) + (driver/connection-error-messages :database-name-incorrect) #"^No suitable driver found for.*$" - (i/connection-error-messages :invalid-hostname) + (driver/connection-error-messages :invalid-hostname) #"^Connection refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.$" - (i/connection-error-messages :cannot-connect-check-host-and-port) + (driver/connection-error-messages :cannot-connect-check-host-and-port) #"^FATAL: role \".*\" does not exist$" - (i/connection-error-messages :username-incorrect) + (driver/connection-error-messages :username-incorrect) #"^FATAL: password authentication failed for user.*$" - (i/connection-error-messages :password-incorrect) + (driver/connection-error-messages :password-incorrect) #"^FATAL: .*$" ; all other FATAL messages: strip off the 'FATAL' part, capitalize, and add a period (let [[_ message] (re-matches #"^FATAL: (.*$)" message)] diff --git a/src/metabase/driver/query_processor/expand.clj b/src/metabase/driver/query_processor/expand.clj index fb1e3260e9073ffbde22fe9960bada756a101ace..1ea9947becbba0a1a5602cc45922350e062ee670 100644 --- a/src/metabase/driver/query_processor/expand.clj +++ b/src/metabase/driver/query_processor/expand.clj @@ -9,7 +9,6 @@ [korma.core :as k] [swiss.arrows :refer [-<>]] [metabase.db :refer [sel]] - [metabase.driver.interface :as driver] [metabase.driver.query-processor.interface :refer :all] [metabase.util :as u]) (:import (clojure.lang Keyword))) diff --git a/src/metabase/driver/sync.clj b/src/metabase/driver/sync.clj index d0dbafe4b96865d56a60b39b9d517ee517be2d94..e2431503f47341ac9bf0fcffef261709cc127906 100644 --- a/src/metabase/driver/sync.clj +++ b/src/metabase/driver/sync.clj @@ -8,8 +8,8 @@ [korma.core :as k] [medley.core :as m] [metabase.db :refer :all] - (metabase.driver [interface :refer [max-sync-lazy-seq-results]] - [query-processor :as qp]) + [metabase.driver :refer [max-sync-lazy-seq-results]] + [metabase.driver.query-processor :as qp] [metabase.driver.sync.queries :as queries] [metabase.events :as events] (metabase.models [common :as common] diff --git a/test/metabase/driver/sync_test.clj b/test/metabase/driver/sync_test.clj index 8307fa680798e9488e15406b4ba7f6cb74c1f78b..dca5fb13e9d9b0aadbd0a4ea0898d85d17399c4d 100644 --- a/test/metabase/driver/sync_test.clj +++ b/test/metabase/driver/sync_test.clj @@ -4,7 +4,6 @@ [metabase.db :refer :all] [metabase.driver :as driver] (metabase.driver [h2 :as h2] - [interface :as i] [sync :as sync]) [metabase.driver.generic-sql.util :refer [korma-entity]] (metabase.models [field :refer [Field]]