Skip to content

Commit d06ce9e

Browse files
authored
Merge pull request #29 from tegonal/dev/TRD_update_to_metabase_0.45.3
Dev/trd update to metabase 0.45.3
2 parents 33a2983 + d992855 commit d06ce9e

File tree

3 files changed

+104
-14
lines changed

3 files changed

+104
-14
lines changed

deps.edn

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,17 @@
1010
org.clojure/clojure {:mvn/version "1.11.1"}
1111
expectations/clojure-test {:mvn/version "1.2.1"}
1212
; replace also the version in metabase-plugin.yaml
13-
metabase/metabase-core {:git/url "https://github.com/metabase/metabase.git" :git/tag "v0.43.0" :git/sha "ee686fcfe5"}
14-
metabase/build-drivers {:git/url "https://github.com/metabase/metabase.git" :git/tag "v0.43.0" :git/sha "ee686fcfe5" :deps/root "bin/build-drivers"}
13+
metabase/metabase-core {:git/url "https://github.com/metabase/metabase.git" :git/tag "v0.45.3" :git/sha "070f57b"}
14+
metabase/build-drivers {:git/url "https://github.com/metabase/metabase.git" :git/tag "v0.45.3" :git/sha "070f57b" :deps/root "bin/build-drivers"}
1515
}
1616

17+
;; These are needed for the Athena and Redshift drivers in order to build them. Maven repos from subprojects do not
18+
;; get copied over -- see
19+
;; https://ask.clojure.org/index.php/10726/deps-manifest-dependencies-respect-repos-dependent-project
20+
:mvn/repos
21+
{"athena" {:url "https://s3.amazonaws.com/maven-athena"}
22+
"redshift" {:url "https://s3.amazonaws.com/redshift-maven-repository/release"}}
23+
1724
; build the driver with `clojure -X:build :project-dir "\"$(pwd)\""`
1825
:aliases {
1926
:build {
@@ -32,4 +39,4 @@
3239
:main-opts ["-m" "cognitect.test-runner"]
3340
:exec-fn cognitect.test-runner.api/test}
3441
}
35-
}
42+
}

resources/metabase-plugin.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
info:
22
name: Metabase Teradata Driver
33
# also replace the version in deps.edn if you change the metabase version here
4-
version: 1.1.0-metabase-v0.43.0-teradata-jdbc-17.10
4+
version: 1.1.0-metabase-v0.45.3-teradata-jdbc-17.10
55
description: Allows Metabase to connect to Teradata databases. Community Supported driver.
66
dependencies:
77
- class: com.teradata.jdbc.TeraDriver
@@ -37,7 +37,7 @@ driver:
3737
- additional-options
3838
- placeholder: e.g. COPLAST=OFF
3939
# required in order that `Choose when syncs and scans happen` shows up in database options afterwards
40-
# (during first time setup, you only see `Periodically refingerprint tables` in metabase v0.43.0
40+
# (during first time setup, you only see `Periodically refingerprint tables` in metabase v0.45.3
4141
# the option `Choose when syncs and scans happen` only shows up later in admin -> Database -> advanced options)
4242
- default-advanced-options
4343
connection-properties-include-tunnel-config: false

src/metabase/driver/teradata.clj

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
(:require [clojure
33
[set :as set]
44
[string :as s]]
5+
[medley.core :as m]
56
[clojure.tools.logging :as log]
67
[clojure.java.jdbc :as jdbc]
78
[honeysql.core :as hsql]
@@ -10,18 +11,20 @@
1011
[driver :as driver]
1112
[util :as u]
1213
[config :as config]]
13-
[metabase.driver.sql-jdbc
14-
[common :as sql-jdbc.common]
15-
[connection :as sql-jdbc.conn]
16-
[execute :as sql-jdbc.execute]
17-
[sync :as sql-jdbc.sync]]
14+
[metabase.driver.sql-jdbc.common :as sql-jdbc.common]
15+
[metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
16+
[metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
17+
[metabase.driver.sql-jdbc.sync :as sql-jdbc.sync]
18+
[metabase.driver.sql-jdbc.sync.common :as sql-jdbc.sync.common]
19+
[metabase.driver.sql-jdbc.sync.interface :as sql-jdbc.sync.interface]
20+
[metabase.driver.sql-jdbc.sync.describe-table :as sql-jdbc.describe-table]
1821
[metabase.driver.sql.query-processor :as sql.qp]
1922
[metabase.driver.sql.util.deduplicate :as deduplicateutil]
2023
[metabase.query-processor.util :as qputil]
2124
[metabase.util
2225
[honeysql-extensions :as hx]
2326
[i18n :refer [trs]]])
24-
(:import [java.sql DatabaseMetaData ResultSet Types PreparedStatement]
27+
(:import [java.sql Connection DatabaseMetaData ResultSet Types PreparedStatement]
2528
[java.time OffsetDateTime OffsetTime]
2629
[java.util Calendar TimeZone]))
2730

@@ -88,6 +91,86 @@
8891
[dbnames]
8992
(when dbnames
9093
(set (map #(s/trim %) (s/split (s/trim dbnames) #",")))))
94+
(defn- jdbc-fields-metadata
95+
"Reducible metadata about the Fields belonging to a Table, fetching using JDBC DatabaseMetaData methods."
96+
[driver ^Connection conn db-name-or-nil schema table-name]
97+
(sql-jdbc.sync.common/reducible-results
98+
#(.getColumns (.getMetaData conn)
99+
db-name-or-nil
100+
(some->> schema (driver/escape-entity-name-for-metadata driver))
101+
(some->> table-name (driver/escape-entity-name-for-metadata driver))
102+
nil)
103+
(fn [^ResultSet rs]
104+
#(let [default (.getString rs "COLUMN_DEF")
105+
no-default? (contains? #{nil "NULL" "null"} default)
106+
nullable (.getInt rs "NULLABLE")
107+
not-nullable? (= 0 nullable)
108+
column-name (.getString rs "COLUMN_NAME")
109+
required? (and no-default? not-nullable?)]
110+
(merge
111+
{:name column-name
112+
:database-type (.getString rs "TYPE_NAME")
113+
:database-required required?}
114+
(when-let [remarks (.getString rs "REMARKS")]
115+
(when-not (s/blank? remarks)
116+
{:field-comment remarks})))))))
117+
118+
(defn fallback-fields-metadata-from-select-query
119+
"In some rare cases `:column_name` is blank (eg. SQLite's views with group by) fallback to sniffing the type from a
120+
SELECT * query."
121+
[driver ^Connection conn table-schema table-name]
122+
(let [[sql & params] (sql-jdbc.sync.interface/fallback-metadata-query driver table-schema table-name)]
123+
(reify clojure.lang.IReduceInit
124+
(reduce [_ rf init]
125+
(with-open [stmt (sql-jdbc.sync.common/prepare-statement driver conn sql params)
126+
rs (.executeQuery stmt)]
127+
(let [metadata (.getMetaData rs)]
128+
(reduce
129+
((map (fn [^Integer i]
130+
{:name (.getColumnName metadata i)
131+
:database-type (.getColumnTypeName metadata i)})) rf)
132+
init
133+
(range 1 (inc (.getColumnCount metadata))))))))))
134+
135+
(defn ^:private fields-metadata
136+
[driver ^Connection conn {schema :schema, table-name :name} ^String db-name-or-nil]
137+
{:pre [(instance? Connection conn) (string? table-name)]}
138+
(reify clojure.lang.IReduceInit
139+
(reduce [_ rf init]
140+
;; 1. Return all the Fields that come back from DatabaseMetaData that include type info.
141+
;;
142+
;; 2. Iff there are some Fields that don't have type info, concatenate
143+
;; `fallback-fields-metadata-from-select-query`, which fetches the same Fields using a different method.
144+
;;
145+
;; 3. Filter out any duplicates between the two methods using `m/distinct-by`.
146+
(let [has-fields-without-type-info? (volatile! false)
147+
jdbc-metadata (eduction
148+
(remove (fn [{:keys [database-type]}]
149+
(when (s/blank? database-type)
150+
(vreset! has-fields-without-type-info? true)
151+
true)))
152+
(jdbc-fields-metadata driver conn db-name-or-nil schema table-name))
153+
fallback-metadata (reify clojure.lang.IReduceInit
154+
(reduce [_ rf init]
155+
(reduce
156+
rf
157+
init
158+
(when @has-fields-without-type-info?
159+
(fallback-fields-metadata-from-select-query driver conn schema table-name)))))]
160+
;; VERY IMPORTANT! DO NOT REWRITE THIS TO BE LAZY! IT ONLY WORKS BECAUSE AS NORMAL-FIELDS GETS REDUCED,
161+
;; HAS-FIELDS-WITHOUT-TYPE-INFO? WILL GET SET TO TRUE IF APPLICABLE AND THEN FALLBACK-FIELDS WILL RUN WHEN
162+
;; IT'S TIME TO START EVALUATING THAT.
163+
(reduce
164+
((comp cat (m/distinct-by :name)) rf)
165+
init
166+
[jdbc-metadata fallback-metadata])))))
167+
168+
(defmethod sql-jdbc.describe-table/describe-table-fields :teradata
169+
[driver conn table db-name-or-nil]
170+
(into
171+
#{}
172+
(sql-jdbc.describe-table/describe-table-fields-xf driver table)
173+
(fields-metadata driver conn table db-name-or-nil)))
91174

92175
(defn- teradata-spec
93176
"Create a database specification for a teradata database. Opts should include keys
@@ -244,7 +327,7 @@
244327
(fn []
245328
(when-let [value (.getTimestamp rs i)]
246329
(.toLocalDateTime value))))
247-
330+
248331
(defmethod sql-jdbc.execute/read-column-thunk [:teradata Types/TIMESTAMP_WITH_TIMEZONE]
249332
[_ rs _ i]
250333
(fn []
@@ -262,7 +345,7 @@
262345
(fn []
263346
(when-let [value (.getTime rs i)]
264347
(.toLocalTime value))))
265-
348+
266349
(defmethod sql-jdbc.execute/read-column-thunk [:teradata Types/TIME_WITH_TIMEZONE]
267350
[_ rs _ i]
268351
(fn []
@@ -330,4 +413,4 @@
330413
((some-fn :db
331414
:dbname
332415
:sid
333-
:catalog))))})
416+
:catalog))))})

0 commit comments

Comments
 (0)