Skip to content
Snippets Groups Projects
Unverified Commit 43a6e10e authored by Ngoc Khuat's avatar Ngoc Khuat Committed by GitHub
Browse files

Fix inconsistence temporal extract for timestamptz columns for h2 (#26824)

parent d1019fc7
No related merge requests found
......@@ -194,3 +194,7 @@
(defmethod tx/supports-time-type? :athena
[_driver]
false)
(defmethod tx/supports-timestamptz-type? :athena
[_driver]
false)
......@@ -13,6 +13,7 @@
(tx/add-test-extensions! :mongo)
(defmethod tx/supports-time-type? :mongo [_driver] false)
(defmethod tx/supports-timestamptz-type? :mongo [_driver] false)
(defn ssl-required?
"Returns if the mongo server requires an SSL connection."
......
......@@ -6,6 +6,8 @@
(sql-jdbc.tx/add-test-extensions! :sqlite)
(defmethod tx/supports-timestamptz-type? :sqlite [_driver] false)
(defmethod tx/dbdef->connection-details :sqlite
[_driver _context dbdef]
{:db (str (tx/escaped-database-name dbdef) ".sqlite")})
......
......@@ -239,22 +239,24 @@
(defn- format-datetime [format-str expr] (hsql/call :formatdatetime expr (hx/literal format-str)))
(defn- parse-datetime [format-str expr] (hsql/call :parsedatetime expr (hx/literal format-str)))
(defn- trunc-with-format [format-str expr] (parse-datetime format-str (format-datetime format-str expr)))
(defn- extract [unit expr] (hsql/call :extract unit (hx/cast :timestamp expr)))
(defmethod sql.qp/date [:h2 :minute] [_ _ expr] (trunc-with-format "yyyyMMddHHmm" expr))
(defmethod sql.qp/date [:h2 :minute-of-hour] [_ _ expr] (hx/minute expr))
(defmethod sql.qp/date [:h2 :hour] [_ _ expr] (trunc-with-format "yyyyMMddHH" expr))
(defmethod sql.qp/date [:h2 :hour-of-day] [_ _ expr] (hx/hour expr))
(defmethod sql.qp/date [:h2 :hour-of-day] [_ _ expr] (extract :hour expr))
(defmethod sql.qp/date [:h2 :day] [_ _ expr] (hx/->date expr))
(defmethod sql.qp/date [:h2 :day-of-month] [_ _ expr] (hsql/call :day_of_month expr))
(defmethod sql.qp/date [:h2 :day-of-year] [_ _ expr] (hsql/call :day_of_year expr))
(defmethod sql.qp/date [:h2 :day-of-month] [_ _ expr] (extract :day expr))
(defmethod sql.qp/date [:h2 :day-of-year] [_ _ expr] (extract :day_of_year expr))
(defmethod sql.qp/date [:h2 :month] [_ _ expr] (trunc-with-format "yyyyMM" expr))
(defmethod sql.qp/date [:h2 :month-of-year] [_ _ expr] (hx/month expr))
(defmethod sql.qp/date [:h2 :quarter-of-year] [_ _ expr] (hx/quarter expr))
(defmethod sql.qp/date [:h2 :year] [_ _ expr] (parse-datetime "yyyy" (hx/year expr)))
(defmethod sql.qp/date [:h2 :month-of-year] [_ _ expr] (extract :month expr))
(defmethod sql.qp/date [:h2 :quarter-of-year] [_ _ expr] (extract :quarter expr))
(defmethod sql.qp/date [:h2 :year] [_ _ expr] (parse-datetime "yyyy" (extract :year expr)))
(defmethod sql.qp/date [:h2 :year-of-era] [_ _ expr] (extract :year expr))
(defmethod sql.qp/date [:h2 :day-of-week]
[_ _ expr]
(sql.qp/adjust-day-of-week :h2 (hsql/call :iso_day_of_week expr)))
(sql.qp/adjust-day-of-week :h2 (extract :iso_day_of_week expr)))
(defmethod sql.qp/date [:h2 :week]
[_ _ expr]
......@@ -262,7 +264,7 @@
(hx/- 1 (sql.qp/date :h2 :day-of-week expr))
:day))
(defmethod sql.qp/date [:h2 :week-of-year-iso] [_ _ expr] (hsql/call :iso_week expr))
(defmethod sql.qp/date [:h2 :week-of-year-iso] [_ _ expr] (extract :iso_week expr))
;; Rounding dates to quarters is a bit involved but still doable. Here's the plan:
;; * extract the year and quarter from the date;
......
......@@ -380,7 +380,7 @@
(mt/dataset sample-dataset
(is (= '{:select [source.PRODUCTS__via__PRODUCT_ID__CATEGORY AS PRODUCTS__via__PRODUCT_ID__CATEGORY
source.PEOPLE__via__USER_ID__SOURCE AS PEOPLE__via__USER_ID__SOURCE
parsedatetime (year (source.CREATED_AT) "yyyy") AS CREATED_AT
parsedatetime (extract (year from CAST (source.CREATED_AT AS timestamp)) "yyyy") AS CREATED_AT
source.pivot-grouping AS pivot-grouping
count (*) AS count]
:from [{:select [ORDERS.ID AS ID
......@@ -408,16 +408,16 @@
AND
(source.PRODUCTS__via__PRODUCT_ID__CATEGORY = ? OR source.PRODUCTS__via__PRODUCT_ID__CATEGORY = ?)
AND
source.CREATED_AT >= parsedatetime (year (dateadd ("year" CAST (-2 AS long) now ())) "yyyy")
source.CREATED_AT >= parsedatetime (extract (year from CAST (dateadd ("year" CAST (-2 AS long) now ()) AS timestamp)) "yyyy")
AND
source.CREATED_AT < parsedatetime (year (now ()) "yyyy"))]
source.CREATED_AT < parsedatetime (extract (year from CAST (now () AS timestamp)) "yyyy"))]
:group-by [source.PRODUCTS__via__PRODUCT_ID__CATEGORY
source.PEOPLE__via__USER_ID__SOURCE
parsedatetime (year (source.CREATED_AT) "yyyy")
parsedatetime (extract (year from CAST (source.CREATED_AT AS timestamp)) "yyyy")
source.pivot-grouping]
:order-by [source.PRODUCTS__via__PRODUCT_ID__CATEGORY ASC
source.PEOPLE__via__USER_ID__SOURCE ASC
parsedatetime (year (source.CREATED_AT) "yyyy") ASC
parsedatetime (extract (year from CAST (source.CREATED_AT AS timestamp)) "yyyy") ASC
source.pivot-grouping ASC]}
(-> (mt/mbql-query orders
{:aggregation [[:aggregation-options [:count] {:name "count"}]]
......
......@@ -112,7 +112,7 @@
(testing (format "extract %s function works as expected on %s column for driver %s" op col-type driver/*driver*)
(is (= (set (expected-fn op)) (set (test-temporal-extract (query-fn op field-id)))))))))
;; mongo doesn't supports cast string to date
;; mongo doesn't supports cast string to date
(mt/test-drivers (disj (mt/normal-drivers-with-feature :temporal-extract) :mongo)
(testing "with date columns"
(doseq [[col-type field-id] [[:date (mt/id :times :d)] [:text-as-date (mt/id :times :as_d)]]
......@@ -140,7 +140,54 @@
:fields (into [] (for [op ops] [:expression (name op)]))})
(mt/formatted-rows (repeat int))
first
(zipmap ops)))))))))
(zipmap ops)))))))
(testing "with timestamptz columns"
(mt/test-drivers (filter mt/supports-timestamptz-type? (mt/normal-drivers-with-feature :temporal-extract))
(mt/with-report-timezone-id "Asia/Kabul"
(is (= (if (or (= driver/*driver* :sqlserver)
(driver/supports? driver/*driver* :set-timezone))
{:get-year 2004,
:get-quarter 1,
:get-month 1,
:get-day 1,
:get-day-of-week 5,
;; TIMEZONE FIXME these drivers are returning the extracted hours in
;; the timezone that they were inserted in
;; maybe they need explicit convert-timezone to the report-tz before extraction?
:get-hour (case driver/*driver*
(:sqlserver :presto :presto-jdbc :snowflake :oracle) 5
2),
:get-minute (case driver/*driver*
(:sqlserver :presto :presto-jdbc :snowflake :oracle) 19
49),
:get-second 9}
{:get-year 2003,
:get-quarter 4,
:get-month 12,
:get-day 31,
:get-day-of-week 4,
:get-hour 22,
:get-minute 19,
:get-second 9})
(let [ops [:get-year :get-quarter :get-month :get-day
:get-day-of-week :get-hour :get-minute :get-second]]
(->> (mt/mbql-query times {:expressions (into {"shifted-day" [:datetime-subtract $dt_tz 78 :day]
;; the idea is to extract a column with value = 2004-01-01 02:49:09 +04:30
;; this way the UTC value is 2003-12-31 22:19:09 +00:00 which will make sure
;; the year, quarter, month, day, week is extracted correctly
;; TODO: it's better to use a literal for this, but the function is not working properly
;; with OffsetDatetime for all drivers, so we'll go wit this for now
"shifted-hour" [:datetime-subtract [:expression "shifted-day"] 4 :hour]}
(for [op ops]
[(name op) [op [:expression "shifted-hour"]]]))
:fields (into [] (for [op ops] [:expression (name op)]))
:filter [:= $index 1]
:limit 1})
mt/process-query
(mt/formatted-rows (repeat int))
first
(zipmap ops))))))))))
(deftest temporal-extraction-with-filter-expresion-tests
(mt/test-drivers (mt/normal-drivers-with-feature :temporal-extract)
......
......@@ -249,7 +249,8 @@
has-test-extensions?
metabase-instance
sorts-nil-first?
supports-time-type?]
supports-time-type?
supports-timestamptz-type?]
[tx.env
set-test-drivers!
......
......@@ -364,6 +364,14 @@
(defmethod supports-time-type? ::test-extensions [_driver] true)
(defmulti supports-timestamptz-type?
"Whether this database supports a `timestamp with time zone` data type or equivalent."
{:arglists '([driver])}
dispatch-on-driver-with-test-extensions
:hierarchy #'driver/hierarchy)
(defmethod supports-timestamptz-type? ::test-extensions [_driver] true)
(defmulti aggregate-column-info
"Return the expected type information that should come back for QP results as part of `:cols` for an aggregation of a
given type (and applied to a given Field, when applicable)."
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment