Skip to content

Commit

Permalink
Fix long lines in metabase.driver.generic-sql
Browse files Browse the repository at this point in the history
  • Loading branch information
camsaul committed Nov 21, 2017
1 parent 6a08763 commit 68193d6
Showing 1 changed file with 63 additions and 44 deletions.
107 changes: 63 additions & 44 deletions src/metabase/driver/generic_sql.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
(ns metabase.driver.generic-sql
"Shared code for drivers for SQL databases using their respective JDBC drivers under the hood."
(:require [clojure
[set :as set]
[string :as str]]
Expand Down Expand Up @@ -31,16 +32,20 @@
Methods marked *OPTIONAL* have default implementations in `ISQLDriverDefaultsMixin`."

(active-tables ^java.util.Set [this, ^DatabaseMetaData metadata]
"*OPTIONAL* Return a set of maps containing information about the active tables/views, collections, or equivalent that currently exist in DATABASE.
Each map should contain the key `:name`, which is the string name of the table. For databases that have a concept of schemas,
this map should also include the string name of the table's `:schema`.
Two different implementations are provided in this namespace: `fast-active-tables` (the default), and `post-filtered-active-tables`. You should be fine using
the default, but refer to the documentation for those functions for more details on the differences.")

;; The following apply-* methods define how the SQL Query Processor handles given query clauses. Each method is called when a matching clause is present
;; in QUERY, and should return an appropriately modified version of KORMA-QUERY. Most drivers can use the default implementations for all of these methods,
;; but some may need to override one or more (e.g. SQL Server needs to override the behavior of `apply-limit`, since T-SQL uses `TOP` instead of `LIMIT`).
"*OPTIONAL* Return a set of maps containing information about the active tables/views, collections, or equivalent
that currently exist in DATABASE. Each map should contain the key `:name`, which is the string name of the table.
For databases that have a concept of schemas, this map should also include the string name of the table's
`:schema`.
Two different implementations are provided in this namespace: `fast-active-tables` (the default), and
`post-filtered-active-tables`. You should be fine using the default, but refer to the documentation for those
functions for more details on the differences.")

;; The following apply-* methods define how the SQL Query Processor handles given query clauses. Each method is
;; called when a matching clause is present in QUERY, and should return an appropriately modified version of
;; `HONEYSQL-FORM`. Most drivers can use the default implementations for all of these methods, but some may need to
;; override one or more (e.g. SQL Server needs to override the behavior of `apply-limit`, since T-SQL uses `TOP`
;; instead of `LIMIT`).
(apply-aggregation [this honeysql-form, ^Map query] "*OPTIONAL*.")
(apply-breakout [this honeysql-form, ^Map query] "*OPTIONAL*.")
(apply-fields [this honeysql-form, ^Map query] "*OPTIONAL*.")
Expand All @@ -61,57 +66,64 @@
"Given a `Database` DETAILS-MAP, return a JDBC connection spec.")

(current-datetime-fn [this]
"*OPTIONAL*. HoneySQL form that should be used to get the current `DATETIME` (or equivalent). Defaults to `:%now`.")
"*OPTIONAL*. HoneySQL form that should be used to get the current `DATETIME` (or equivalent). Defaults to
`:%now`.")

(date [this, ^Keyword unit, field-or-value]
"Return a HoneySQL form for truncating a date or timestamp field or value to a given resolution, or extracting a date component.")
"Return a HoneySQL form for truncating a date or timestamp field or value to a given resolution, or extracting a
date component.")

(excluded-schemas ^java.util.Set [this]
"*OPTIONAL*. Set of string names of schemas to skip syncing tables from.")

(field->identifier [this, ^FieldInstance field]
"*OPTIONAL*. Return a HoneySQL form that should be used as the identifier for FIELD.
The default implementation returns a keyword generated by from the components returned by `field/qualified-name-components`.
Other drivers like BigQuery need to do additional qualification, e.g. the dataset name as well.
(At the time of this writing, this is only used by the SQL parameters implementation; in the future it will probably be used in more places as well.)")
The default implementation returns a keyword generated by from the components returned by
`field/qualified-name-components`. Other drivers like BigQuery need to do additional qualification, e.g. the
dataset name as well. (At the time of this writing, this is only used by the SQL parameters implementation; in
the future it will probably be used in more places as well.)")

(field->alias ^String [this, ^Field field]
"*OPTIONAL*. Return the alias that should be used to for FIELD, i.e. in an `AS` clause. The default implementation calls `name`, which
returns the *unqualified* name of `Field`.
"*OPTIONAL*. Return the alias that should be used to for FIELD, i.e. in an `AS` clause. The default implementation
calls `name`, which returns the *unqualified* name of `Field`.
Return `nil` to prevent FIELD from being aliased.")

(prepare-sql-param [this obj]
"*OPTIONAL*. Do any neccesary type conversions, etc. to an object being passed as a prepared statment argument in a parameterized raw SQL query.
For example, a raw SQL query with a date param, `x`, e.g. `WHERE date > {{x}}`, is converted to SQL like `WHERE date > ?`, and the value of
`x` is passed as a `java.sql.Timestamp`. Some databases, notably SQLite, don't work with `Timestamps`, and dates must be passed as string literals
instead; the SQLite driver overrides this method to convert dates as needed.
"*OPTIONAL*. Do any neccesary type conversions, etc. to an object being passed as a prepared statment argument in
a parameterized raw SQL query. For example, a raw SQL query with a date param, `x`, e.g. `WHERE date > {{x}}`, is
converted to SQL like `WHERE date > ?`, and the value of `x` is passed as a `java.sql.Timestamp`. Some databases,
notably SQLite, don't work with `Timestamps`, and dates must be passed as string literals instead; the SQLite
driver overrides this method to convert dates as needed.
The default implementation is `identity`.
NOTE - This method is only used for parameters in raw SQL queries. It's not needed for MBQL queries because other functions like `prepare-value` are
used for similar purposes; at some point in the future, we might be able to combine them into a single method used in both places.")
NOTE - This method is only used for parameters in raw SQL queries. It's not needed for MBQL queries because other
functions like `prepare-value` are used for similar purposes; at some point in the future, we might be able to
combine them into a single method used in both places.")

(prepare-value [this, ^Value value]
"*OPTIONAL*. Prepare a value (e.g. a `String` or `Integer`) that will be used in a HoneySQL form. By default, this returns VALUE's `:value` as-is, which
is eventually passed as a parameter in a prepared statement. Drivers such as BigQuery that don't support prepared statements can skip this
behavior by returning a HoneySQL `raw` form instead, or other drivers can perform custom type conversion as appropriate.")
"*OPTIONAL*. Prepare a value (e.g. a `String` or `Integer`) that will be used in a HoneySQL form. By default, this
returns VALUE's `:value` as-is, which is eventually passed as a parameter in a prepared statement. Drivers such
as BigQuery that don't support prepared statements can skip this behavior by returning a HoneySQL `raw` form
instead, or other drivers can perform custom type conversion as appropriate.")

(quote-style ^clojure.lang.Keyword [this]
"*OPTIONAL*. Return the quoting style that should be used by [HoneySQL](https://github.com/jkk/honeysql) when building a SQL statement.
Defaults to `:ansi`, but other valid options are `:mysql`, `:sqlserver`, `:oracle`, and `:h2` (added in `metabase.util.honeysql-extensions`;
like `:ansi`, but uppercases the result).
"*OPTIONAL*. Return the quoting style that should be used by [HoneySQL](https://github.com/jkk/honeysql) when
building a SQL statement. Defaults to `:ansi`, but other valid options are `:mysql`, `:sqlserver`, `:oracle`, and
`:h2` (added in `metabase.util.honeysql-extensions`; like `:ansi`, but uppercases the result).
(hsql/format ... :quoting (quote-style driver))")

(set-timezone-sql ^String [this]
"*OPTIONAL*. This should be a format string containing a SQL statement to be used to set the timezone for the current transaction.
The `%s` will be replaced with a string literal for a timezone, e.g. `US/Pacific`.
"*OPTIONAL*. This should be a format string containing a SQL statement to be used to set the timezone for the
current transaction. The `%s` will be replaced with a string literal for a timezone, e.g. `US/Pacific.`
\"SET @@session.timezone = %s;\"")

(stddev-fn ^clojure.lang.Keyword [this]
"*OPTIONAL*. Keyword name of the SQL function that should be used to do a standard deviation aggregation. Defaults to `:STDDEV`.")
"*OPTIONAL*. Keyword name of the SQL function that should be used to do a standard deviation aggregation. Defaults
to `:STDDEV`.")

(string-length-fn ^clojure.lang.Keyword [this, ^Keyword field-key]
"Return a HoneySQL form appropriate for getting the length of a `Field` identified by fully-qualified FIELD-KEY.
Expand All @@ -120,8 +132,9 @@
(hsql/call :length (hx/cast :VARCHAR field-key))")

(unix-timestamp->timestamp [this, field-or-value, ^Keyword seconds-or-milliseconds]
"Return a HoneySQL form appropriate for converting a Unix timestamp integer field or value to an proper SQL `Timestamp`.
SECONDS-OR-MILLISECONDS refers to the resolution of the int in question and with be either `:seconds` or `:milliseconds`."))
"Return a HoneySQL form appropriate for converting a Unix timestamp integer field or value to an proper SQL
`Timestamp`. SECONDS-OR-MILLISECONDS refers to the resolution of the int in question and with be either
`:seconds` or `:milliseconds`."))


;; This does something important for the Crate driver, apparently (what?)
Expand Down Expand Up @@ -181,9 +194,9 @@
"If DETAILS contains an `:addtional-options` key, append those options to the connection string in CONNECTION-SPEC.
(Some drivers like MySQL provide this details field to allow special behavior where needed).
Optionally specify SEPERATOR-STYLE, which defaults to `:url` (e.g. `?a=1&b=2`). You may instead set it to `:semicolon`,
which will separate different options with semicolons instead (e.g. `;a=1;b=2`). (While most drivers require the former
style, some require the latter.)"
Optionally specify SEPERATOR-STYLE, which defaults to `:url` (e.g. `?a=1&b=2`). You may instead set it to
`:semicolon`, which will separate different options with semicolons instead (e.g. `;a=1;b=2`). (While most drivers
require the former style, some require the latter.)"
{:arglists '([connection-spec] [connection-spec details & {:keys [seperator-style]}])}
;; single arity provided for cases when `connection-spec` is built by applying simple transformations to `details`
([connection-spec]
Expand Down Expand Up @@ -232,7 +245,8 @@
:quoting (quote-style driver)
:allow-dashed-names? true))
(catch Throwable e
(log/error (u/format-color 'red "Invalid HoneySQL form:\n%s" (u/pprint-to-str honeysql-form)))
(log/error (u/format-color 'red "Invalid HoneySQL form:\n%s"
(u/pprint-to-str honeysql-form)))
(throw e)))]
(into [(hx/unescape-dots sql)] args)))

Expand Down Expand Up @@ -288,10 +302,12 @@
(into-array String ["TABLE", "VIEW", "FOREIGN TABLE", "MATERIALIZED VIEW"]))))

(defn fast-active-tables
"Default, fast implementation of `ISQLDriver/active-tables` best suited for DBs with lots of system tables (like Oracle).
Fetch list of schemas, then for each one not in `excluded-schemas`, fetch its Tables, and combine the results.
"Default, fast implementation of `ISQLDriver/active-tables` best suited for DBs with lots of system tables (like
Oracle). Fetch list of schemas, then for each one not in `excluded-schemas`, fetch its Tables, and combine the
results.
This is as much as 15x faster for Databases with lots of system tables than `post-filtered-active-tables` (4 seconds vs 60)."
This is as much as 15x faster for Databases with lots of system tables than `post-filtered-active-tables` (4
seconds vs 60)."
[driver, ^DatabaseMetaData metadata]
(let [all-schemas (set (map :table_schem (jdbc/result-set-seq (.getSchemas metadata))))
schemas (set/difference all-schemas (excluded-schemas driver))]
Expand All @@ -316,7 +332,8 @@
(merge {:name column_name
:custom {:column-type type_name}
:base-type (or (column->base-type driver (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))
(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/*))}
(when calculated-special-type
(assert (isa? calculated-special-type :type/*)
Expand Down Expand Up @@ -361,8 +378,10 @@
[]
(require 'metabase.driver.generic-sql.query-processor)
{:active-tables fast-active-tables
:apply-aggregation (resolve 'metabase.driver.generic-sql.query-processor/apply-aggregation) ; don't resolve the vars yet so during interactive dev if the
:apply-breakout (resolve 'metabase.driver.generic-sql.query-processor/apply-breakout) ; underlying impl changes we won't have to reload all the drivers
;; don't resolve the vars yet so during interactive dev if the underlying impl changes we won't have to reload all
;; the drivers
:apply-aggregation (resolve 'metabase.driver.generic-sql.query-processor/apply-aggregation)
:apply-breakout (resolve 'metabase.driver.generic-sql.query-processor/apply-breakout)
:apply-fields (resolve 'metabase.driver.generic-sql.query-processor/apply-fields)
:apply-filter (resolve 'metabase.driver.generic-sql.query-processor/apply-filter)
:apply-join-tables (resolve 'metabase.driver.generic-sql.query-processor/apply-join-tables)
Expand Down

0 comments on commit 68193d6

Please sign in to comment.