Skip to content

Commit

Permalink
Try to enable Foreign Keys ClickHouse#9 (ClickHouse#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
enqueue authored Oct 7, 2019
1 parent 1bf0b42 commit e818549
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 26 deletions.
54 changes: 30 additions & 24 deletions src/metabase/driver/clickhouse.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[clojure.string :as str]
[honeysql.core :as hsql]
[metabase
[config :as config]
[driver :as driver]
[util :as u]]
[metabase.driver
Expand Down Expand Up @@ -180,6 +181,12 @@
[driver [_ field]]
(hsql/call :stddevSamp (sql.qp/->honeysql driver field)))

(defmethod sql.qp/->honeysql [:clickhouse :count]
[driver [_ field]]
(if field
(hsql/call :count (sql.qp/->honeysql driver field))
:%count))

(defmethod sql.qp/->honeysql [:clickhouse :/]
[driver args]
(let [args (for [arg args]
Expand Down Expand Up @@ -247,25 +254,6 @@
(defmethod sql.qp/->honeysql [:clickhouse :ends-with] [driver [_ field value options]]
(ch-like-clause driver (sql.qp/->honeysql driver field) (update-string-value value #(str \% %)) options))

;; ClickHouse aliases are globally usable. Once an alias is introduced, we
;; can not refer to the same field by qualified name again, unless we mean
;; it. See https://clickhouse.yandex/docs/en/query_language/syntax/#peculiarities-of-use
;; We add a suffix to make the reference in the query unique.
(defmethod sql.qp/field->alias :clickhouse [_ field]
(str (:name field) "_mb_alias"))

;; See above. We are removing the artificial alias suffix
(defmethod driver/execute-query :clickhouse [driver query]
(update-in
(sql-jdbc.execute/execute-query driver query)
[:columns]
(fn [columns]
(mapv (fn [column]
(if (str/ends-with? column "_mb_alias")
(subs column 0 (str/last-index-of column "_mb_alias"))
column))
columns))))

(defmethod sql-jdbc.execute/read-column [:clickhouse Types/TIMESTAMP] [driver calendar resultset meta i]
(when-let [timestamp (.getTimestamp resultset i)]
(if (str/starts-with? (.toString timestamp) "1970-01-01")
Expand Down Expand Up @@ -322,11 +310,29 @@

(defmethod driver/display-name :clickhouse [_] "ClickHouse")

(defmethod driver/supports? [:clickhouse :foreign-keys] [_ _] false)

;; TODO: Nested queries are actually supported, but I do not know how
;; to make the driver use correct aliases per sub-query
(defmethod driver/supports? [:clickhouse :nested-queries] [_ _] false)
;; For tests only: Get FK info via some metadata table
(defmethod driver/describe-table-fks :clickhouse
[driver db-or-id-or-spec table & [^String db-name-or-nil]]
(if config/is-test?
(let [db-name (if (nil? db-name-or-nil)
(:name db-or-id-or-spec)
db-name-or-nil)]
(try
(let [fks (jdbc/query
(->spec db-or-id-or-spec)
[(str/join ["SELECT * FROM `"
(if (str/blank? db-name) "default" db-name)
"-mbmeta`"])])]
(let [myset (set
(for [fk fks]
{:fk-column-name (:fk_source_column fk)
:dest-table {:name (:fk_dest_table fk)
:schema (if (str/blank? db-name) "default" db-name)}
:dest-column-name (:fk_dest_column fk)}))]
myset))
(catch Exception e
(driver/describe-table-fks :sql-jdbc db-or-id-or-spec table))))
nil))

(defmethod driver/date-add :clickhouse
[_ dt amount unit]
Expand Down
35 changes: 33 additions & 2 deletions test/metabase/test/data/clickhouse.clj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

(defmethod sql.tx/field-base-type->sql-type [:clickhouse :type/Time] [_ _] "DateTime")

(defmethod tx/sorts-nil-first? :clickhouse [_] false)

(defmethod tx/dbdef->connection-details :clickhouse [_ context {:keys [database-name]}]
(merge
{:host (tx/db-test-env-var-or-throw :clickhouse :host "localhost")
Expand Down Expand Up @@ -67,6 +69,35 @@
(defmethod load-data/load-data! :clickhouse [& args]
(apply load-data/load-data-add-ids! args))

(defmethod sql.tx/add-fk-sql :clickhouse [& _] nil)
(defmethod sql.tx/pk-sql-type :clickhouse [_] "Int32")

(defmethod sql.tx/pk-sql-type :clickhouse [_] "UInt32")
;; For FK testing: We use some metadata table
(defmethod sql.tx/add-fk-sql :clickhouse
[driver {:keys [database-name]} {:keys [table-name]} {dest-table-name :fk, field-name :field-name}]
(let [quot #(sql.u/quote-name driver %1 (tx/format-name driver %2))
dest-table-name (name dest-table-name)]
(format "CREATE TABLE IF NOT EXISTS %s (
fk_name String,
fk_db String,
fk_source_table String,
fk_source_column String,
fk_dest_table String,
fk_dest_column String
) ENGINE=Memory;
-- now insert the FK data
INSERT INTO %s (
fk_name,
fk_db,
fk_source_table,
fk_source_column,
fk_dest_table,
fk_dest_column
) VALUES ('%s', '%s', '%s', '%s', '%s', '%s');"
(sql.tx/qualify-and-quote driver (str database-name "-mbmeta"))
(sql.tx/qualify-and-quote driver (str database-name "-mbmeta"))
(apply str (take 30 (format "fk_%s_%s_%s" table-name field-name dest-table-name)))
database-name
table-name
field-name
dest-table-name
(sql.tx/pk-field-name driver))))

0 comments on commit e818549

Please sign in to comment.