From e5b34121999c619b7052828d1ca6707714c5df8d Mon Sep 17 00:00:00 2001 From: Cam Saul <cam@geotip.com> Date: Wed, 8 Apr 2015 16:14:25 -0700 Subject: [PATCH] thou can setteth thy timezones --- project.clj | 3 +- src/metabase/driver/generic_sql/native.clj | 34 ++++++++++++++++--- .../driver/generic_sql/query_processor.clj | 2 +- .../driver/postgres/query_processor.clj | 8 +++-- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/project.clj b/project.clj index 0ab876e2939..ffb35b49c86 100644 --- a/project.clj +++ b/project.clj @@ -66,7 +66,8 @@ [lein-bikeshed "0.2.0"] ; Linting [lein-expectations "0.0.8"] ; run unit tests with 'lein expectations' [lein-instant-cheatsheet "2.1.1"] ; use awesome instant cheatsheet created by yours truly w/ 'lein instant-cheatsheet' - [lein-marginalia "0.8.0"]] ; generate documentation with 'lein marg' + [lein-marginalia "0.8.0"] ; generate documentation with 'lein marg' + [refactor-nrepl "1.0.1"]] :jvm-opts ["-Dlogfile.path=target/log" "-Xms1024m" ; give JVM a decent heap size to start with "-Xmx2048m" ; hard limit of 2GB so we stop hitting the 4GB container limit on CircleCI diff --git a/src/metabase/driver/generic_sql/native.clj b/src/metabase/driver/generic_sql/native.clj index c79b531d1ba..79b93a9921d 100644 --- a/src/metabase/driver/generic_sql/native.clj +++ b/src/metabase/driver/generic_sql/native.clj @@ -3,13 +3,13 @@ (:import com.metabase.corvus.api.ApiException) (:require [clojure.java.jdbc :as jdbc] [clojure.tools.logging :as log] - [korma.core :as korma] - korma.db + (korma [core :as korma] + db) [metabase.db :refer [sel]] [metabase.driver.generic-sql.util :refer :all] [metabase.models.database :refer [Database]])) -(def class->base-type +(def ^:const class->base-type "Map of classes returned from DB call to metabase.models.field/base-types" {java.lang.Boolean :BooleanField java.lang.Double :FloatField @@ -22,6 +22,18 @@ java.sql.Date :DateField java.sql.Timestamp :DateTimeField}) +(def ^:dynamic *timezone->set-timezone-sql* + " This function is called whenever `timezone` is specified in a native query, at the beginning of + the DB transaction. + + If implemented, it should take a timestamp like `\"US/Pacific\"` and return a SQL + string that can be executed to set the timezone within the context of a transaction, + e.g. `\"SET LOCAL timezone to 'US/Pacific'\"`. + + Because not all DB engines support timestamps (e.g., H2), the default implementation is a no-op. + Engines that *do* support timestamps (e.g., Postgres) should override this function." + (fn [_])) + (defn- value->base-type "Attempt to match a value we get back from the DB with the corresponding base-type`." [v] @@ -29,6 +41,7 @@ (or (class->base-type (type v)) (throw (ApiException. (int 500) (format "Missing base type mapping for %s in metabase.driver.generic-sql.native/class->base-type. Please add an entry." (str (type v)))))))) + (defn process-and-run "Process and run a native (raw SQL) QUERY." {:arglists '([query])} @@ -36,11 +49,18 @@ database-id :database :as query}] {:pre [(string? sql) (integer? database-id)]} - (log/debug "QUERY: " query) + (log/debug "QUERY: \n" + (with-out-str (clojure.pprint/pprint query))) (try (let [db (-> (sel :one Database :id database-id) korma-db korma.db/get-connection) - [columns & [first-row :as rows]] (jdbc/query db sql :as-arrays? true)] + [columns & [first-row :as rows]] (jdbc/with-db-transaction [conn db :read-only? true] + ;; If timezone is specified in the Query and the driver supports setting the timezone then execute SQL to set it + (when-let [timezone (-> query :native :timezone)] + (when-let [set-timezone-sql (*timezone->set-timezone-sql* timezone)] + (log/debug "Setting timezone to:" timezone) + (jdbc/db-do-prepared conn set-timezone-sql))) + (jdbc/query conn sql :as-arrays? true))] {:status :completed :row_count (count rows) :data {:rows rows @@ -55,3 +75,7 @@ (re-find #"^(.*);") ; the user already knows the SQL, and error code is meaningless second) ; so just return the part of the exception that is relevant (.getMessage e))}))) + +(def db (delay (-> (sel :one Database :id 1) + korma-db + korma.db/get-connection))) diff --git a/src/metabase/driver/generic_sql/query_processor.clj b/src/metabase/driver/generic_sql/query_processor.clj index a5820bfbcfa..b380ac2fdb8 100644 --- a/src/metabase/driver/generic_sql/query_processor.clj +++ b/src/metabase/driver/generic_sql/query_processor.clj @@ -19,7 +19,7 @@ query-is-cumulative-sum? apply-cumulative-sum) -(def ^{:dynamic true, :private true} *query* +(def ^:dynamic ^:private *query* "Query dictionary that we're currently processing" nil) diff --git a/src/metabase/driver/postgres/query_processor.clj b/src/metabase/driver/postgres/query_processor.clj index 28b2cb40e2c..54a9b311371 100644 --- a/src/metabase/driver/postgres/query_processor.clj +++ b/src/metabase/driver/postgres/query_processor.clj @@ -1,6 +1,10 @@ (ns metabase.driver.postgres.query-processor - (:require [metabase.driver.generic-sql.query-processor :as generic] + (:require (metabase.driver.generic-sql [native :as native] + [query-processor :as generic]) [metabase.driver :refer [process-and-run]])) + (defmethod process-and-run :postgres [query] - (generic/process-and-run query)) + (binding [native/*timezone->set-timezone-sql* (fn [timezone] + (format "SET LOCAL timezone TO '%s';" timezone))] + (generic/process-and-run query))) -- GitLab