diff --git a/docs/administration-guide/databases/cratedb.md b/docs/administration-guide/databases/cratedb.md index dcc6f5da89561b51876db3810b8a325069ad55d1..10864759412c4e9b516080acb1db35ec1e27278a 100644 --- a/docs/administration-guide/databases/cratedb.md +++ b/docs/administration-guide/databases/cratedb.md @@ -15,4 +15,8 @@ Starting in v0.18.0 Metabase provides a driver for connecting to CrateDB directl 3. Click the `Save` button. Done. -Metabase will now begin inspecting your CrateDB Dataset and finding any tables and fields to build up a sense for the schema. Give it a little bit of time to do its work and then you're all set to start querying. \ No newline at end of file +Metabase will now begin inspecting your CrateDB Dataset and finding any tables and fields to build up a sense for the schema. Give it a little bit of time to do its work and then you're all set to start querying. + +### Known limitations + +* Columns/Fields of type `object_array` are deactivated and not exposed. However, their nested fields are listed and also supported for queries. \ No newline at end of file diff --git a/src/metabase/driver/crate.clj b/src/metabase/driver/crate.clj index 6d88591a90c6febca681fce0017ddc09e696d769..89ee9509f849094429f8e1bab4869631aa72c4f2 100644 --- a/src/metabase/driver/crate.clj +++ b/src/metabase/driver/crate.clj @@ -1,10 +1,12 @@ (ns metabase.driver.crate (:require [clojure.java.jdbc :as jdbc] + [clojure.tools.logging :as log] [honeysql.core :as hsql] [metabase.driver :as driver] [metabase.driver.crate.util :as crate-util] [metabase.driver.generic-sql :as sql] - [metabase.util :as u])) + [metabase.util :as u]) + (:import java.sql.DatabaseMetaData)) (def ^:private ^:const column->base-type "Map of Crate column types -> Field base types @@ -55,6 +57,39 @@ (defn- string-length-fn [field-key] (hsql/call :char_length field-key)) +(defn- describe-table-fields + [database, driver, {:keys [schema name]}] + (let [columns (jdbc/query + (sql/db->jdbc-connection-spec database) + [(format "select column_name, data_type as type_name + from information_schema.columns + where table_name like '%s' and table_schema like '%s' + and data_type != 'object_array'" name schema)])] ; clojure jdbc can't handle fields of type "object_array" atm + (set (for [{:keys [column_name type_name]} columns] + (merge {:name column_name + :custom {:column-type type_name} + :base-type (or (column->base-type (keyword type_name)) + (do (log/warn (format "Don't know how to map column type '%s' to a Field base_type, falling back to :type/*." type_name)) + :type/*))}))))) + +(defn- add-table-pks + [^DatabaseMetaData metadata, table] + (let [pks (->> (.getPrimaryKeys metadata nil nil (:name table)) + jdbc/result-set-seq + (mapv :column_name) + set)] + (update table :fields (fn [fields] + (set (for [field fields] + (if-not (contains? pks (:name field)) + field + (assoc field :pk? true)))))))) + +(defn- describe-table [driver database table] + (sql/with-metadata [metadata driver database] + (->> (describe-table-fields database driver table) + (assoc (select-keys table [:name :schema]) :fields) + ;; find PKs and mark them + (add-table-pks metadata)))) (defrecord CrateDriver [] clojure.lang.Named @@ -65,6 +100,7 @@ (merge (sql/IDriverSQLDefaultsMixin) {:can-connect? (u/drop-first-arg can-connect?) :date-interval crate-util/date-interval + :describe-table describe-table :details-fields (constantly [{:name "hosts" :display-name "Hosts" :default "localhost:5432"}]) @@ -75,6 +111,8 @@ :column->base-type (u/drop-first-arg column->base-type) :string-length-fn (u/drop-first-arg string-length-fn) :date crate-util/date + :quote-style (constantly :crate) + :field->alias (constantly nil) :unix-timestamp->timestamp crate-util/unix-timestamp->timestamp :current-datetime-fn (constantly now)})) diff --git a/src/metabase/util/honeysql_extensions.clj b/src/metabase/util/honeysql_extensions.clj index 5b21d722754cf15f64f30df2d3a4e9a6e56e6331..fa95415f6346e958dc0fdda2cb6318c0423d0fb8 100644 --- a/src/metabase/util/honeysql_extensions.clj +++ b/src/metabase/util/honeysql_extensions.clj @@ -21,6 +21,23 @@ (intern 'honeysql.format 'quote-fns (assoc quote-fns :h2 (comp s/upper-case ansi-quote-fn)))) +(defn- str-insert + "Insert c in string s at index i." + [s c i] + (str c (subs s 0 i) c (subs s i))) + +(defn- crate-column-identifier + [^CharSequence s] + (let [idx (s/index-of s "[")] + (if (nil? idx) + (str \" s \") + (str-insert s "\"" idx)))) + +;; `:crate` quote style that correctly quotes nested column identifiers +(let [quote-fns @(resolve 'honeysql.format/quote-fns)] + (->> (assoc quote-fns :crate crate-column-identifier) + (intern 'honeysql.format 'quote-fns))) + ;; register the `extract` function with HoneySQL ;; (hsql/format (hsql/call :extract :a :b)) -> "extract(a from b)" (defmethod hformat/fn-handler "extract" [_ unit expr]