22 (:require [clojure
33 [set :as set]
44 [string :as s]]
5- [medley.core :as m]
5+ [medley.core :as m]
66 [clojure.tools.logging :as log]
77 [clojure.java.jdbc :as jdbc]
88 [java-time :as t]
2020 [metabase.driver.sql.query-processor :as sql.qp]
2121 [metabase.driver.sql.util.deduplicate :as deduplicateutil]
2222 [metabase.util.i18n :refer [trs]]
23- [metabase.util.honey-sql-2 :as h2x]
24- )
23+ [metabase.util.honey-sql-2 :as h2x])
2524 (:import [java.sql Connection DatabaseMetaData ResultSet Types PreparedStatement]
2625 [java.time OffsetDateTime OffsetTime]
2726 [java.util Calendar TimeZone]))
2827
2928(driver/register! :teradata , :parent :sql-jdbc )
3029
3130(defmethod sql-jdbc.sync /database-type->base-type :teradata [_ column-type]
32- ({:BIGINT :type/BigInteger
33- :BIGSERIAL :type/BigInteger
34- :BIT :type/*
35- :BLOB :type/*
36- :BOX :type/*
37- :CHAR :type/Text
38- :CLOB :type/Text
39- :BYTE :type/*
40- :BYTEINT :type/Integer
41- :DATE :type/Date
42- :DECIMAL :type/Decimal
43- :FLOAT :type/Float
44- :FLOAT4 :type/Float
45- :FLOAT8 :type/Float
46- :INTEGER :type/Integer
47- :INT :type/Integer
48- :INT2 :type/Integer
49- :INT4 :type/Integer
50- :INT8 :type/BigInteger
51- :INTERVAL :type/* ; time span
52- :JSON :type/Text
53- :LONGVARCHAR :type/Text ; Teradata extension
54- :LSEG :type/*
55- :MACADDR :type/Text
56- :MONEY :type/Decimal
57- :NUMERIC :type/Decimal
58- :PATH :type/*
59- :POINT :type/*
60- :REAL :type/Float
61- :SERIAL :type/Integer
62- :SERIAL2 :type/Integer
63- :SERIAL4 :type/Integer
64- :SERIAL8 :type/BigInteger
65- :SMALLINT :type/Integer
66- :SMALLSERIAL :type/Integer
67- :TIME :type/Time
31+ ({:BIGINT :type/BigInteger
32+ :BIGSERIAL :type/BigInteger
33+ :BIT :type/*
34+ :BLOB :type/*
35+ :BOX :type/*
36+ :CHAR :type/Text
37+ :CLOB :type/Text
38+ :BYTE :type/*
39+ :BYTEINT :type/Integer
40+ :DATE :type/Date
41+ :DECIMAL :type/Decimal
42+ :FLOAT :type/Float
43+ :FLOAT4 :type/Float
44+ :FLOAT8 :type/Float
45+ :INTEGER :type/Integer
46+ :INT :type/Integer
47+ :INT2 :type/Integer
48+ :INT4 :type/Integer
49+ :INT8 :type/BigInteger
50+ :INTERVAL :type/* ; time span
51+ :JSON :type/Text
52+ :LONGVARCHAR :type/Text ; Teradata extension
53+ :LSEG :type/*
54+ :MACADDR :type/Text
55+ :MONEY :type/Decimal
56+ :NUMERIC :type/Decimal
57+ :PATH :type/*
58+ :POINT :type/*
59+ :REAL :type/Float
60+ :SERIAL :type/Integer
61+ :SERIAL2 :type/Integer
62+ :SERIAL4 :type/Integer
63+ :SERIAL8 :type/BigInteger
64+ :SMALLINT :type/Integer
65+ :SMALLSERIAL :type/Integer
66+ :TIME :type/Time
6867 (keyword " TIME WITH TIME ZONE" ) :type/Time
69- :TIMESTAMP :type/DateTime
70- (keyword " TIMESTAMP WITH TIME ZONE" ) :type/DateTime
71- :TSQUERY :type/*
72- :TSVECTOR :type/*
73- :TXID_SNAPSHOT :type/*
74- :UUID :type/UUID
75- :VARBIT :type/*
76- :VARBYTE :type/* ; byte array
77- :VARCHAR :type/Text
78- :XML :type/Text
68+ :TIMESTAMP :type/DateTime
69+ (keyword " TIMESTAMP WITH TIME ZONE" ) :type/DateTime
70+ :TSQUERY :type/*
71+ :TSVECTOR :type/*
72+ :TXID_SNAPSHOT :type/*
73+ :UUID :type/UUID
74+ :VARBIT :type/*
75+ :VARBYTE :type/* ; byte array
76+ :VARCHAR :type/Text
77+ :XML :type/Text
7978 (keyword " bit varying" ) :type/*
8079 (keyword " character varying" ) :type/Text
8180 (keyword " double precision" ) :type/Float
103102 (some->> table-name (driver/escape-entity-name-for-metadata driver))
104103 nil )
105104 (fn [^ResultSet rs]
106- #(let [default (.getString rs " COLUMN_DEF" )
107- no-default? (contains? #{nil " NULL" " null" } default )
108- nullable (.getInt rs " NULLABLE" )
109- not-nullable? (= 0 nullable)
110- column-name (.getString rs " COLUMN_NAME" )
111- required? (and no-default? not-nullable?)]
105+ #(let [default (.getString rs " COLUMN_DEF" )
106+ no-default? (contains? #{nil " NULL" " null" } default )
107+ nullable (.getInt rs " NULLABLE" )
108+ not-nullable? (= 0 nullable)
109+ column-name (.getString rs " COLUMN_NAME" )
110+ required? (and no-default? not-nullable?)]
112111 (merge
113- {:name column-name
114- :database-type (.getString rs " TYPE_NAME" )
115- :database-required required?}
112+ {:name column-name
113+ :database-type (.getString rs " TYPE_NAME" )
114+ :database-required required?}
116115 (when-let [remarks (.getString rs " REMARKS" )]
117116 (when-not (s/blank? remarks)
118117 {:field-comment remarks})))))))
125124 (reify clojure.lang.IReduceInit
126125 (reduce [_ rf init]
127126 (with-open [stmt (sql-jdbc.sync.common/prepare-statement driver conn sql params)
128- rs (.executeQuery stmt)]
127+ rs (.executeQuery stmt)]
129128 (let [metadata (.getMetaData rs)]
130129 (reduce
131- ((map (fn [^Integer i]
132- {:name (.getColumnName metadata i)
133- :database-type (.getColumnTypeName metadata i)})) rf)
134- init
135- (range 1 (inc (.getColumnCount metadata))))))))))
130+ ((map (fn [^Integer i]
131+ {:name (.getColumnName metadata i)
132+ :database-type (.getColumnTypeName metadata i)})) rf)
133+ init
134+ (range 1 (inc (.getColumnCount metadata))))))))))
136135
137136(defn ^:private fields-metadata
138137 [driver ^Connection conn {schema :schema , table-name :name } ^String db-name-or-nil]
146145 ; ;
147146 ; ; 3. Filter out any duplicates between the two methods using `m/distinct-by`.
148147 (let [has-fields-without-type-info? (volatile! false )
149- jdbc-metadata (eduction
150- (remove (fn [{:keys [database-type]}]
151- (when (s/blank? database-type)
152- (vreset! has-fields-without-type-info? true )
153- true )))
154- (jdbc-fields-metadata driver conn db-name-or-nil schema table-name))
155- fallback-metadata (reify clojure.lang.IReduceInit
156- (reduce [_ rf init]
157- (reduce
158- rf
159- init
160- (when @has-fields-without-type-info?
161- (fallback-fields-metadata-from-select-query driver conn schema table-name)))))]
148+ jdbc-metadata (eduction
149+ (remove (fn [{:keys [database-type]}]
150+ (when (s/blank? database-type)
151+ (vreset! has-fields-without-type-info? true )
152+ true )))
153+ (jdbc-fields-metadata driver conn db-name-or-nil schema table-name))
154+ fallback-metadata (reify clojure.lang.IReduceInit
155+ (reduce [_ rf init]
156+ (reduce
157+ rf
158+ init
159+ (when @has-fields-without-type-info?
160+ (fallback-fields-metadata-from-select-query driver conn schema table-name)))))]
162161 ; ; VERY IMPORTANT! DO NOT REWRITE THIS TO BE LAZY! IT ONLY WORKS BECAUSE AS NORMAL-FIELDS GETS REDUCED,
163162 ; ; HAS-FIELDS-WITHOUT-TYPE-INFO? WILL GET SET TO TRUE IF APPLICABLE AND THEN FALLBACK-FIELDS WILL RUN WHEN
164163 ; ; IT'S TIME TO START EVALUATING THAT.
196195 ; ; We don't need lob support in metabase. This also removes the limitation of 16 open statements per session which would interfere metadata crawling.
197196 " LOB_SUPPORT" " OFF"
198197 }
199- (if ssl
200- {" SSLMODE" " REQUIRE" }))
201- (map #(format " %s=%s" (first %) (second %)))
202- (clojure.string/join " ," )))}
198+ (if ssl
199+ {" SSLMODE" " REQUIRE" }))
200+ (map #(format " %s=%s" (first %) (second %)))
201+ (clojure.string/join " ," )))}
203202 (dissoc opts :host :port :dbnames :tmode :charset :ssl :encrypt-data )))
204203
205204(defmethod sql-jdbc.conn /connection-details->spec :teradata
206205 [_ details-map]
207206 (->
208- ; ; :engine, :let-user-control-scheduling and :advanced-options are part of the details-map but would lead to
209- ; ; java.sql.SQLException: [Teradata JDBC Driver] [TeraJDBC 17.10.00.27] [Error 1536] [SQLState HY000] Invalid connection parameter name advanced-options
210- ; ; thus we filtering the map, using only the data we are interested in teradata-spec
211- ; ; (more keys might be added in the future to `default-advanced-options` => see metabase-plugin.yaml
212- ; ; thus we switched from using `dissoc` to `select-keys`)
213- (select-keys details-map [:host :port :user :password :dbnames :charset :tmode :encrypt-data :ssl :additional-options ])
207+ ; ; :engine, :let-user-control-scheduling and :advanced-options are part of the details-map but would lead to
208+ ; ; java.sql.SQLException: [Teradata JDBC Driver] [TeraJDBC 17.10.00.27] [Error 1536] [SQLState HY000] Invalid connection parameter name advanced-options
209+ ; ; thus we filtering the map, using only the data we are interested in teradata-spec
210+ ; ; (more keys might be added in the future to `default-advanced-options` => see metabase-plugin.yaml
211+ ; ; thus we switched from using `dissoc` to `select-keys`)
212+ (select-keys details-map [:host :port :user :password :dbnames :charset :tmode :encrypt-data :ssl :additional-options ])
214213 teradata-spec
215214 (sql-jdbc.common/handle-additional-options details-map, :seperator-style :comma )))
216215
234233(defmethod sql.qp /date [:teradata :week ] [_ _ expr] (h2x/->date (date-trunc :day expr))) ; Same behaviour as with Oracle.
235234(defmethod sql.qp /date [:teradata :week-of-year ] [_ _ expr] (h2x/inc (h2x// (h2x/- (date-trunc :iw expr)
236235 (date-trunc :iy expr))
237- 7 )))
238- (defmethod sql.qp /date [:teradata :month ] [_ _ expr] (date-trunc :mm expr))
239- (defmethod sql.qp /date [:teradata :month-of-year ] [_ _ expr] [::h2x/extract :month expr])
240- (defmethod sql.qp /date [:teradata :quarter ] [_ _ expr] (date-trunc :q expr))
236+ 7 )))
237+ (defmethod sql.qp /date [:teradata :month ] [_ _ expr] (date-trunc :mm expr))
238+ (defmethod sql.qp /date [:teradata :month-of-year ] [_ _ expr] [::h2x/extract :month expr])
239+ (defmethod sql.qp /date [:teradata :quarter ] [_ _ expr] (date-trunc :q expr))
241240(defmethod sql.qp /date [:teradata :quarter-of-year ] [driver _ expr] (h2x// (h2x/+ (sql.qp/date driver :month-of-year (sql.qp/date driver :quarter expr)) 2 ) 3 ))
242- (defmethod sql.qp /date [:teradata :year ] [_ _ expr] (date-trunc :year expr))
241+ (defmethod sql.qp /date [:teradata :year ] [_ _ expr] (date-trunc :year expr))
243242
244243(defn- num-to-interval [unit amount]
245244 (:raw (format " INTERVAL '%d' %s" (int (Math/abs amount)) (name unit))))
251250 (date-trunc :month hsql-form)
252251 (hsql-form ))
253252 (case unit
254- :second (num-to-interval :second amount)
255- :minute (num-to-interval :minute amount)
256- :hour (num-to-interval :hour amount)
257- :day (num-to-interval :day amount)
258- :week (num-to-interval :day (* amount 7 ))
259- :month (num-to-interval :month amount)
260- :quarter (num-to-interval :month (* amount 3 ))
261- :year (num-to-interval :year amount)))))
253+ :second (num-to-interval :second amount)
254+ :minute (num-to-interval :minute amount)
255+ :hour (num-to-interval :hour amount)
256+ :day (num-to-interval :day amount)
257+ :week (num-to-interval :day (* amount 7 ))
258+ :month (num-to-interval :month amount)
259+ :quarter (num-to-interval :month (* amount 3 ))
260+ :year (num-to-interval :year amount)))))
262261
263262(defmethod sql.qp /unix-timestamp->honeysql [:teradata :seconds ] [_ _ field-or-value]
264263 (:to_timestamp field-or-value))
273272
274273(defmethod sql.qp /apply-top-level-clause [:teradata :page ] [_ _ honeysql-form {{:keys [items page]} :page }]
275274 (assoc honeysql-form :offset (:raw (format " QUALIFY ROW_NUMBER() OVER (%s) BETWEEN %d AND %d"
276- (first (: format (select-keys honeysql-form [:order-by ])
277- :allow-dashed-names? true
278- :quoting :ansi ))
279- (inc (* items (dec page)))
280- (* items page)))))
275+ (first (format (select-keys honeysql-form [:order-by ])
276+ :allow-dashed-names? true
277+ :quoting :ansi ))
278+ (inc (* items (dec page)))
279+ (* items page)))))
281280
282281(def excluded-schemas
283282 #{" SystemFe" " SYSLIB" " LockLogShredder" " Sys_Calendar" " SYSBAR" " SYSUIF"
295294 " Fetch a JDBC Metadata ResultSet of tables in the DB, optionally limited to ones belonging to a given schema."
296295 ^ResultSet [^DatabaseMetaData metadata, ^String schema-or-nil]
297296 (jdbc/result-set-seq (.getTables metadata nil schema-or-nil " %" ; tablePattern "%" = match all tables
298- (into-array String [" TABLE" , " VIEW" , " FOREIGN TABLE" ]))))
297+ (into-array String [" TABLE" , " VIEW" , " FOREIGN TABLE" ]))))
299298
300299(defn- fast-active-tables
301300 " Teradata, fast implementation of `fast-active-tables` to support inclusion list."
302301 [driver, ^DatabaseMetaData metadata, {{:keys [dbnames]} :details , :as database}]
303302 (let [all-schemas (set (map :table_schem (jdbc/result-set-seq (.getSchemas metadata))))
304303 dbs (dbnames-set dbnames)
305- schemas (if (empty? dbs)
306- (set/difference all-schemas excluded-schemas) ; use default exclusion list
307- (set/intersection all-schemas dbs))] ; use defined inclusion list
304+ schemas (if (empty? dbs)
305+ (set/difference all-schemas excluded-schemas) ; use default exclusion list
306+ (set/intersection all-schemas dbs))] ; use defined inclusion list
308307 (set (for [schema schemas
309308 table-name (mapv :table_name (get-tables metadata schema))]
310309 {:name table-name
314313; ; which will be used to filter the schemas.
315314(defmethod driver /describe-database :teradata [driver database]
316315 (jdbc/with-db-metadata [metadata (sql-jdbc.conn/db->pooled-connection-spec database)]
317- {:tables (fast-active-tables, driver, ^DatabaseMetaData metadata, database)}))
316+ {:tables (fast-active-tables, driver, ^DatabaseMetaData metadata, database)}))
318317
319318; ; We can't use getObject(int, Class) as the underlying Resultset used by the Teradata jdbc driver is based on jdk6.
320319(defmethod sql-jdbc.execute /read-column-thunk [:teradata Types/TIMESTAMP]
351350(defmethod sql-jdbc.execute /set-parameter [:teradata OffsetDateTime]
352351 [_ ^PreparedStatement ps ^Integer i t]
353352 (let [cal (Calendar/getInstance (TimeZone/getTimeZone (t/zone-id t)))
354- t (t/sql-timestamp t)]
353+ t (t/sql-timestamp t)]
355354 (.setTimestamp ps i t cal)))
356355
357356; ; Run the query itself without setting the timezone connection parameter as this must not be changed on a Teradata connection.
426425(defmethod sql-jdbc.conn /data-warehouse-connection-pool-properties :teradata
427426 [driver database]
428427 {
429- " acquireRetryDelay" (or (config/config-int :mb-jdbc-c3po-acquire-retry-delay ) 1000 )
428+ " acquireRetryDelay" (or (config/config-int :mb-jdbc-c3po-acquire-retry-delay ) 1000 )
430429 " acquireIncrement" 1
431- " maxIdleTime" (* 3 60 60 ) ; 3 hours
430+ " maxIdleTime" (* 3 60 60 ) ; 3 hours
432431 " minPoolSize" 1
433432 " initialPoolSize" 1
434433 " maxPoolSize" (or (config/config-int :mb-jdbc-data-warehouse-max-connection-pool-size ) 15 )
439438 ((some-fn :db
440439 :dbname
441440 :sid
442- :catalog ))))})
441+ :catalog ))))})
0 commit comments