Skip to content

Commit

Permalink
datetime-diff function for redshift (metabase#26758) (metabase#27067)
Browse files Browse the repository at this point in the history
* Add redshift implementation

* Add failing tests

* Fix bigquery

* Formatting

* fix? set-param for redshift

* Replace literals for datetime-diff-time-zones-test

* Actually use fields

* Fix typo

* Rename dataset

* Add type/DateTimeWithTZ column, since redshift doesn't like type/DateTimeWithZoneOffset

* Cast to timestamp for week diff

* Revert "fix? set-param for redshift"

This reverts commit 235f3ac.

* Fix leap years

* Remove redundant date-trunc

* Formatting

* Fix mismatched types

* Fix extract

* Add comment for why redshift needs an implementation

Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>

Co-authored-by: Cal Herries <39073188+calherries@users.noreply.github.com>
Co-authored-by: Ngoc Khuat <qn.khuat@gmail.com>
  • Loading branch information
3 people committed Dec 9, 2022
1 parent 4c6bfba commit df8b912
Showing 1 changed file with 53 additions and 9 deletions.
62 changes: 53 additions & 9 deletions modules/drivers/redshift/src/metabase/driver/redshift.clj
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,6 @@
(or (database-type->base-type column-type)
((get-method sql-jdbc.sync/database-type->base-type :postgres) driver column-type)))

(defmethod driver/database-supports? [:redshift :datetime-diff]
[_driver _feat _db]
;; postgres uses `date_part` on an interval or a call to `age` to get datediffs. It seems redshift does not have
;; this and errors with:
;; > ERROR: function pg_catalog.pgdate_part("unknown", interval) does not exist
;; It offers a datediff function that tracks number of boundaries, which could be used to implement the correct behaviour,
;; similar to bigquery.
false)

(defmethod sql.qp/add-interval-honeysql-form :redshift
[_ hsql-form amount unit]
(let [hsql-form (hx/->timestamp hsql-form)]
Expand Down Expand Up @@ -202,6 +193,59 @@
(map (partial sql.qp/->honeysql driver))
(reduce (partial hsql/call :concat))))

(defn- extract [unit temporal]
(hsql/call :extract (format "'%s'" (name unit)) temporal))

(defmethod sql.qp/->honeysql [:redshift :datetime-diff]
;; postgres uses `extract` around a call to `age` to calculate datediffs.
;; redshift doesn't have `age`, so we have to use `extract` instead.
[driver [_ x y unit]]
(let [x (hx/->timestamp (sql.qp/->honeysql driver x))
y (hx/->timestamp (sql.qp/->honeysql driver y))]
(case unit
:year
(let [positive-diff (fn [a b] ; precondition: a <= b
(hx/-
(hx/- (extract :year b) (extract :year a))
;; decrement if a is later than b in the year calendar
(hx/cast
:integer
(hsql/call
:or
(hsql/call :> (extract :month a) (extract :month b))
(hsql/call
:and
(hsql/call := (extract :month a) (extract :month b))
(hsql/call :> (extract :day a) (extract :day b)))))))]
(hsql/call :case (hsql/call :<= x y) (positive-diff x y) :else (hx/* -1 (positive-diff y x))))

:month
(let [positive-diff (fn [a b] ; precondition: a <= b
(hx/-
(hsql/call :datediff (hsql/raw (name unit)) a b)
(hx/cast :integer (hsql/call :> (extract :day a) (extract :day b)))))]
(hsql/call :case (hsql/call :<= x y) (positive-diff x y) :else (hx/* -1 (positive-diff y x))))

:week
(let [positive-diff (fn [a b]
(hx/cast
:integer
(hx/floor
(hx// (hsql/call :datediff (hsql/raw "day") a b) 7))))]
(hsql/call :case (hsql/call :<= x y) (positive-diff x y) :else (hx/* -1 (positive-diff y x))))

:day
(hsql/call :datediff (hsql/raw (name unit)) x y)

(:hour :minute :second)
(let [positive-diff (fn [a b]
(hx/cast
:integer
(hx/floor
(hx// (hx/cast :float (hsql/call :datediff (hsql/raw "millisecond") a b)) ; datediff returns integer, so cast to float
(case unit :hour 3600000 :minute 60000 :second 1000)))))]
(hsql/call :case (hsql/call :<= x y) (positive-diff x y) :else (hx/* -1 (positive-diff y x)))))))

;;; +----------------------------------------------------------------------------------------------------------------+
;;; | metabase.driver.sql-jdbc impls |
;;; +----------------------------------------------------------------------------------------------------------------+
Expand Down

0 comments on commit df8b912

Please sign in to comment.