New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Snowflake/Redshift filtering on date for timestamp with time zone columns #41864
Fix Snowflake/Redshift filtering on date for timestamp with time zone columns #41864
Conversation
(defmethod unprepare/unprepare-value [:snowflake OffsetDateTime] | ||
[_ t] | ||
(format "'%s %s %s'::timestamp_tz" (t/local-date t) (t/local-time t) (t/zone-offset t))) | ||
|
||
(defmethod unprepare/unprepare-value [:snowflake ZonedDateTime] | ||
[driver t] | ||
(unprepare/unprepare-value driver (t/offset-date-time t))) | ||
|
||
(defmethod unprepare/unprepare-value [:snowflake OffsetTime] | ||
[driver t] | ||
(unprepare/unprepare-value driver (t/local-time (t/with-offset-same-instant t (t/zone-offset 0))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer needed since we don't parameterize these anymore.
;; Like Vertica, Snowflake doesn't seem to be able to return a LocalTime/OffsetTime like everyone else, but it can | ||
;; return a String that we can parse | ||
|
||
(defmethod sql-jdbc.execute/read-column-thunk [:snowflake Types/TIMESTAMP_WITH_TIMEZONE] | ||
[_ ^ResultSet rs _ ^Integer i] | ||
(fn [] | ||
(when-let [s (.getString rs i)] | ||
(let [t (u.date/parse s)] | ||
(log/tracef "(.getString rs %d) [TIMESTAMP_WITH_TIMEZONE] -> %s -> %s" i (pr-str s) (pr-str t)) | ||
t)))) | ||
|
||
(defmethod sql-jdbc.execute/read-column-thunk [:snowflake Types/TIME] | ||
[_ ^ResultSet rs _ ^Integer i] | ||
(fn [] | ||
(when-let [s (.getString rs i)] | ||
(let [t (u.date/parse s)] | ||
(log/tracef "(.getString rs %d) [TIME] -> %s -> %s" i (pr-str s) (pr-str t)) | ||
t)))) | ||
|
||
(defmethod sql-jdbc.execute/read-column-thunk [:snowflake Types/TIME_WITH_TIMEZONE] | ||
[_ ^ResultSet rs _ ^Integer i] | ||
(fn [] | ||
(when-let [s (.getString rs i)] | ||
(let [t (u.date/parse s)] | ||
(log/tracef "(.getString rs %d) [TIME_WITH_TIMEZONE] -> %s -> %s" i (pr-str s) (pr-str t)) | ||
t)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TIME
impl is exactly the same as the version in metabase.driver.sql-jdbc.execute.legacy-impl
, which Snowflake would have used anyway, so I removed it. Snowflake doesn't have TIME_WITH_TIMEZONE
, so this was dead code. But metabase.driver.sql-jdbc.execute.legacy-impl
was missing TIME_WITH_TIMEZONE
and TIMESTAMP_WITH_TIMEZONE
impls so I moved them there since they look almost exactly like the other impls.
;; TODO would it make more sense to use functions like `timestamp_tz_from_parts` directly instead of JDBC parameters? | ||
|
||
;; Snowflake seems to ignore the calendar parameter of `.setTime` and `.setTimestamp` and instead uses the session | ||
;; timezone; normalize temporal values to UTC so we end up with the right values | ||
(defmethod sql-jdbc.execute/set-parameter [::use-legacy-classes-for-read-and-set java.time.OffsetTime] | ||
[driver ps i t] | ||
(sql-jdbc.execute/set-parameter driver ps i (t/sql-time (t/with-offset-same-instant t (t/zone-offset 0))))) | ||
|
||
(defmethod sql-jdbc.execute/set-parameter [::use-legacy-classes-for-read-and-set java.time.OffsetDateTime] | ||
[driver ps i t] | ||
(sql-jdbc.execute/set-parameter driver ps i (t/sql-timestamp (t/with-offset-same-instant t (t/zone-offset 0))))) | ||
|
||
(defmethod sql-jdbc.execute/set-parameter [:snowflake java.time.ZonedDateTime] | ||
[driver ps i t] | ||
(sql-jdbc.execute/set-parameter driver ps i (t/sql-timestamp (t/with-zone-same-instant t (t/zone-id "UTC"))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer needed since we're generating SQL literals instead
(let [[snippet & args] (sql.qp/format-honeysql driver x)] | ||
(let [honeysql (sql.qp/->honeysql driver x) | ||
[snippet & args] (sql.qp/format-honeysql driver honeysql)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parameter compilation code should call ->honeysql
on java.time
arguments
(fn [] | ||
(when-let [s (.getString rs i)] | ||
(let [t (u.date/parse s)] | ||
(log/tracef "(.getString rs i) [TIME] -> %s -> %s" (pr-str s) (pr-str t)) | ||
t)))) | ||
|
||
(defmethod sql-jdbc.execute/read-column-thunk [::use-legacy-classes-for-read-and-set Types/TIME_WITH_TIMEZONE] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved from Snowflake
|
@@ -383,7 +383,7 @@ | |||
also have this issue." | |||
[driver] | |||
;; TIMEZONE FIXME — remove this and fix the drivers | |||
(contains? #{:snowflake :oracle :redshift} driver)) | |||
(contains? #{:oracle :redshift} driver)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring is out of date now.
@camsaul Did you forget to add a milestone to the issue for this PR? When and where should I add a milestone? |
… columns (#41864) * Fix Snowflake filtering on date for `timestamp with time zone` columns * Snowflake should just generate raw SQL for literals * Go ahead and fix other Snowflake stuff and reenable a bunch of disabled tests * Snowflake test fixes 🔧 * Minor test improvements * Fix Redshift and Vertica as well. * Address PR feedback
…with time zone columns" (#42088) * Fix Snowflake/Redshift filtering on date for timestamp with time zone columns (#41864) * Fix Snowflake filtering on date for `timestamp with time zone` columns * Snowflake should just generate raw SQL for literals * Go ahead and fix other Snowflake stuff and reenable a bunch of disabled tests * Snowflake test fixes 🔧 * Minor test improvements * Fix Redshift and Vertica as well. * Address PR feedback * Remove test for fix that isn't part of this PR * Remove stuff only in >= 50 * Backport change to skip feature validity check for namespaced driver features --------- Co-authored-by: Cam Saul <1455846+camsaul@users.noreply.github.com> Co-authored-by: Cam Saul <github@camsaul.com>
Fixes #8804
Fixes #11643
Fixes #19910
Fixes #39769
Fixes #41863
Fixes #41866
Fixes #42073
The Snowflake JDBC driver still doesn't support Java 8
java.time
classes, which means we were setting things likeOffsetDateTime
as ajava.sql.Timestamp
... even worse, it ignores attachedjava.util.Calendar
s (java.sql.Timestamp
+java.util.Calendar
is basically the pre-Java-8 way of setting atimestamp with time zone
value in JDBC)... we actually had code to do the right thing for Snowflake from #11718 but it only worked forZonedDateTime
:https://github.com/metabase/metabase/blob/b93e99d/modules/drivers/snowflake/src/metabase/driver/snowflake.clj?rgh-link-date=2024-04-25T21%3A33%3A43Z#L618-L632
In #40530 I fixed related issues for
date = date
filtering but there were still issues withtimestamp_tz = date
filtering.I ended up changing the Snowflake QP code to just output timestamp literals directly, e.g.
'2024-04-25 17:58:00 -07:00'::timestamp_tz
instead of
? -- a java.sql.Timestamp
so we can be sure it's doing the right thing and so we're no longer at the mercy of a poorly-implemented JDBC driver.
I had to fix some stuff with native query parameter compilation, for whatever reason it wasn't calling
->honeysql
forjava.time
classes and just assuming everyone wanted?
; now it's working correctly.