Skip to content

Commit e7aa10c

Browse files
committed
Get metadata from views
1 parent b943f71 commit e7aa10c

File tree

1 file changed

+77
-121
lines changed

1 file changed

+77
-121
lines changed

src/metabase/driver/teradata.clj

Lines changed: 77 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -31,60 +31,63 @@
3131
(defmethod driver/database-supports? [:teradata feature] [_driver _feature _db] supported?))
3232

3333
(defmethod sql-jdbc.sync/database-type->base-type :teradata [_ column-type]
34-
({:BIGINT :type/BigInteger
35-
:BIGSERIAL :type/BigInteger
36-
:BIT :type/*
37-
:BLOB :type/*
38-
:BOX :type/*
39-
:CHAR :type/Text
40-
:CLOB :type/Text
41-
:BYTE :type/*
42-
:BYTEINT :type/Integer
43-
:DATE :type/Date
44-
:DECIMAL :type/Decimal
45-
:FLOAT :type/Float
46-
:FLOAT4 :type/Float
47-
:FLOAT8 :type/Float
48-
:INTEGER :type/Integer
49-
:INT :type/Integer
50-
:INT2 :type/Integer
51-
:INT4 :type/Integer
52-
:INT8 :type/BigInteger
53-
:INTERVAL :type/* ; time span
54-
:JSON :type/Text
55-
:LONGVARCHAR :type/Text ; Teradata extension
56-
:LSEG :type/*
57-
:MACADDR :type/Text
58-
:MONEY :type/Decimal
59-
:NUMERIC :type/Decimal
60-
:PATH :type/*
61-
:POINT :type/*
62-
:REAL :type/Float
63-
:SERIAL :type/Integer
64-
:SERIAL2 :type/Integer
65-
:SERIAL4 :type/Integer
66-
:SERIAL8 :type/BigInteger
67-
:SMALLINT :type/Integer
68-
:SMALLSERIAL :type/Integer
69-
:TIME :type/Time
70-
(keyword "TIME WITH TIME ZONE") :type/Time
71-
:TIMESTAMP :type/DateTime
72-
(keyword "TIMESTAMP WITH TIME ZONE") :type/DateTime
73-
:TSQUERY :type/*
74-
:TSVECTOR :type/*
75-
:TXID_SNAPSHOT :type/*
76-
:UUID :type/UUID
77-
:VARBIT :type/*
78-
:VARBYTE :type/* ; byte array
79-
:VARCHAR :type/Text
80-
:XML :type/Text
81-
(keyword "bit varying") :type/*
82-
(keyword "character varying") :type/Text
83-
(keyword "double precision") :type/Float
84-
(keyword "time with time zone") :type/Time
85-
(keyword "time without time zone") :type/Time
86-
(keyword "timestamp with timezone") :type/DateTime
87-
(keyword "timestamp without timezone") :type/DateTime}, column-type))
34+
(let [type-mapping
35+
{:BIGINT :type/BigInteger
36+
:BIGSERIAL :type/BigInteger
37+
:BIT :type/*
38+
:BLOB :type/*
39+
:BOX :type/*
40+
:CHAR :type/Text
41+
:CLOB :type/Text
42+
:BYTE :type/*
43+
:BYTEINT :type/Integer
44+
:DATE :type/Date
45+
:DECIMAL :type/Decimal
46+
:FLOAT :type/Float
47+
:FLOAT4 :type/Float
48+
:FLOAT8 :type/Float
49+
:INTEGER :type/Integer
50+
:INT :type/Integer
51+
:INT2 :type/Integer
52+
:INT4 :type/Integer
53+
:INT8 :type/BigInteger
54+
:INTERVAL :type/* ; time span
55+
:JSON :type/Text
56+
:LONGVARCHAR :type/Text ; Teradata extension
57+
:LSEG :type/*
58+
:MACADDR :type/Text
59+
:MONEY :type/Decimal
60+
:NUMERIC :type/Decimal
61+
:NUMBER :type/Decimal ; Add this mapping
62+
:PATH :type/*
63+
:POINT :type/*
64+
:REAL :type/Float
65+
:SERIAL :type/Integer
66+
:SERIAL2 :type/Integer
67+
:SERIAL4 :type/Integer
68+
:SERIAL8 :type/BigInteger
69+
:SMALLINT :type/Integer
70+
:SMALLSERIAL :type/Integer
71+
:TIME :type/Time
72+
(keyword "TIME WITH TIME ZONE") :type/Time
73+
:TIMESTAMP :type/DateTime
74+
(keyword "TIMESTAMP WITH TIME ZONE") :type/DateTime
75+
:TSQUERY :type/*
76+
:TSVECTOR :type/*
77+
:TXID_SNAPSHOT :type/*
78+
:UUID :type/UUID
79+
:VARBIT :type/*
80+
:VARBYTE :type/* ; byte array
81+
:VARCHAR :type/Text
82+
:XML :type/Text
83+
(keyword "bit varying") :type/*
84+
(keyword "character varying") :type/Text
85+
(keyword "double precision") :type/Float
86+
(keyword "time with time zone") :type/Time
87+
(keyword "time without time zone") :type/Time
88+
(keyword "timestamp with timezone") :type/DateTime
89+
(keyword "timestamp without timezone") :type/DateTime}]
90+
(get type-mapping column-type :type/*))) ; Default to :type/* if no mapping is found
8891

8992
(defn- dbnames-set
9093
"Transform the string of databases to a set of strings."
@@ -93,78 +96,31 @@
9396
(set (map #(s/trim %) (s/split (s/trim dbnames) #",")))))
9497

9598
(defn- jdbc-fields-metadata
96-
"Reducible metadata about the Fields belonging to a Table, fetching using JDBC DatabaseMetaData methods."
99+
"Fetch metadata about the Fields belonging to a Table or View using a SELECT * query."
97100
[driver ^Connection conn db-name-or-nil schema table-name]
98-
(sql-jdbc.sync.common/reducible-results
99-
#(.getColumns (.getMetaData conn)
100-
db-name-or-nil
101-
(some->> schema (driver/escape-entity-name-for-metadata driver))
102-
(some->> table-name (driver/escape-entity-name-for-metadata driver))
103-
nil)
104-
(fn [^ResultSet rs]
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?)]
111-
(merge
112-
{:name column-name
113-
:database-type (.getString rs "TYPE_NAME")
114-
:database-required required?}
115-
(when-let [remarks (.getString rs "REMARKS")]
116-
(when-not (s/blank? remarks)
117-
{:field-comment remarks})))))))
118-
119-
(defn fallback-fields-metadata-from-select-query
120-
"In some rare cases `:column_name` is blank (eg. SQLite's views with group by) fallback to sniffing the type from a
121-
SELECT * query."
122-
[driver ^Connection conn db-name-or-nil table-schema table-name]
123-
(let [[sql & params] (sql-jdbc.sync.interface/fallback-metadata-query driver db-name-or-nil table-schema table-name)]
124-
(reify clojure.lang.IReduceInit
125-
(reduce [_ rf init]
126-
(with-open [stmt (sql-jdbc.sync.common/prepare-statement driver conn sql params)
127-
rs (.executeQuery stmt)]
128-
(let [metadata (.getMetaData rs)]
129-
(reduce
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))))))))))
101+
(let [sql (str "SELECT * FROM " (when schema (str schema ".")) table-name " WHERE 1=0")] ; Query with no rows
102+
(with-open [stmt (.createStatement conn)
103+
rs (.executeQuery stmt sql)]
104+
(let [metadata (.getMetaData rs)]
105+
(mapv (fn [i]
106+
(let [column-name (.getColumnName metadata i)
107+
database-type (.getColumnTypeName metadata i)
108+
column-size (.getColumnDisplaySize metadata i)
109+
nullable (.isNullable metadata i)
110+
remarks (.getColumnLabel metadata i)]
111+
{:name column-name
112+
:database-type database-type
113+
:column-size column-size
114+
:nullable? (= nullable DatabaseMetaData/columnNullable)
115+
:remarks remarks}))
116+
(range 1 (inc (.getColumnCount metadata))))))))
135117

136118
(defn ^:private fields-metadata
137119
[driver ^Connection conn {schema :schema, table-name :name} ^String db-name-or-nil]
138120
{:pre [(instance? Connection conn) (string? table-name)]}
139-
(reify clojure.lang.IReduceInit
140-
(reduce [_ rf init]
141-
;; 1. Return all the Fields that come back from DatabaseMetaData that include type info.
142-
;;
143-
;; 2. Iff there are some Fields that don't have type info, concatenate
144-
;; `fallback-fields-metadata-from-select-query`, which fetches the same Fields using a different method.
145-
;;
146-
;; 3. Filter out any duplicates between the two methods using `m/distinct-by`.
147-
(let [has-fields-without-type-info? (volatile! false)
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 db-name-or-nil schema table-name)))))]
161-
;; VERY IMPORTANT! DO NOT REWRITE THIS TO BE LAZY! IT ONLY WORKS BECAUSE AS NORMAL-FIELDS GETS REDUCED,
162-
;; HAS-FIELDS-WITHOUT-TYPE-INFO? WILL GET SET TO TRUE IF APPLICABLE AND THEN FALLBACK-FIELDS WILL RUN WHEN
163-
;; IT'S TIME TO START EVALUATING THAT.
164-
(reduce
165-
((comp cat (m/distinct-by :name)) rf)
166-
init
167-
[jdbc-metadata fallback-metadata])))))
121+
;; Attempt to fetch metadata using DatabaseMetaData.getColumns
122+
(let [jdbc-metadata (jdbc-fields-metadata driver conn db-name-or-nil schema table-name)]
123+
jdbc-metadata))
168124

169125
(defmethod sql-jdbc.describe-table/describe-table-fields :teradata
170126
[driver conn table db-name-or-nil]

0 commit comments

Comments
 (0)