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
User-definable start of week #13090
User-definable start of week #13090
Conversation
Codecov Report
@@ Coverage Diff @@
## master #13090 +/- ##
==========================================
+ Coverage 83.01% 83.05% +0.03%
==========================================
Files 326 326
Lines 25893 25934 +41
Branches 1889 1888 -1
==========================================
+ Hits 21495 21539 +44
+ Misses 2509 2507 -2
+ Partials 1889 1888 -1
Continue to review full report at Codecov.
|
This is a hacky idea, but I wonder if we could avoid driver changes by deducing the offset in the query itself. i.e. do a date diff between |
If nothing else, it's a good backup strategy. You could even just say day of week of truncated now. |
I say yes for consistency / no surprises
I say yes to syncing this regularly - this value comes from either a database setting or the locale the database operates in. (Russian locales use Monday as start of week, for example)
I was thinking per-instance - it seems like this is a business decision, as opposed to an individual user decision. (Imagine the confusion if two different people within the same org send conflicting dashboards because one is using Sunday as SOW and the other is using Monday)
Date truncation in MySQL is funky. This may need to be driver-specific, but the
Hrm. If this is asked during the initial setup, then a session variable might be fine. That said, if it's changed on a running instance with active users, they wouldn't see the change until a logout / login. This is a problem we already have with cached metadata. I think at some point we need a event channel where the backend can send events to the frontend when changes are made. |
Haven't looked thru the code yet, but the approach as you've described it seems pretty solid.
I agree, I think we should leave the MBQL the same for now and just have it behave differently based on the system
I agree that computing it in the query would be more robust and a better approach. I'm hoping in the future we can move away from using session variables entirely and compute timezone offsets directly in the query as well instead of setting session timezones. Session variables are finicky
I think for now doing it per-instance should be fine, we can always make it finer-grained in the future. My preference would be to make it a Setting sort of like
It seems like this query would be pretty quick so we might as well do it on every sync. I guess we'd only need to do this for DBs that support changing the default start of week via a config setting, right?
It seems like we'd only need to implement this method for a few databases that have a specific config setting for default start of week. It seems like in those cases it's easy enough to just have a custom DB-specific implementations to fetch that specific parameter. Maybe the implementations would look something like ;; DB that supports custom start of weeks
(defmethod driver/db-start-of-week :sqlserver
[_ database]
(let [[{:keys [start]}] (jdbc/query (connection-details database) "SELECT @@datefirst AS start")]
(case start
1 :monday
2 :tuesday
...)))
;; hardcode for other DBs
(defmethod driver/db-start-of-week :postgres
[_ _]
:sunday) |
Regarding how granular to make the control over day of the week, I agree with @camsaul that If people beat down our doors asking for per-user, we can revisit. |
I've punted on looking at per-db start of week as it would upturn the entire MBQL processor API (for all drivers) which I don't think it's worth it. This is only an issue for SQL Server, Oracle, and Snowflake; and only if non-default start of week is set. In that case the users might have to set a "wrong" day in settings for the math to work out (eg. Saturday instead of Sunday). The only unresolvable situation is if you have multiple of these DBs with different start of weeks set, but that seems like enough of a corner case and bad practice that it's fine if we punt on this for now. The "correct" solution for this would be to have features per-db rather than per-driver. I've compiled other reasons why we'd want to do this (and a proposal how to do it) here: #13136 |
Potential migration issue: currently drivers are inconsistent in what we return when aggregating by week, some are normalized to Sunday, but not all. Once this gets merged all will work the same which will potentially change what existing cards show. Is this an issue? |
Holding off on fixing Spark SQL until we decide if we bump minimal supported version to 2.3. See also #11023 |
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.
Overall logic/approach look good! I had to scribble down some tables to understand the mods in the adjust functions.
I think it'd be good to add some Cypress tests since there are a few places where the FE and BE need to agree (week ranges and day of week integers).
(- offset) :day) | ||
(truncate-fn expr)))) | ||
|
||
(defn adjust-day-of-week |
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.
I didn't test, but I think this will break in the client. We need to remap the offset numbers to the correct labels based on the setting.
- changeSet: | ||
id: 267 | ||
author: sb | ||
comment: Added 0.37.0 | ||
changes: | ||
- addColumn: | ||
tableName: metabase_database | ||
columns: | ||
- column: | ||
name: start_of_week | ||
type: varchar(32) | ||
defaultValue: sunday | ||
constraints: | ||
nullable: false |
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.
I'm confused why start_of_week
is stored for each db. Do we set it during sync? It looks like each driver hardcodes a value.
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.
This is in preparation for when we do this per-db, but I've punted on this for v1 to limit scope.
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.
Should we remove the migration now and add it back when it's used? There always some risk that future features get punted indefinitely and then we have an extra column sitting around.
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.
if we're not using this yet, might as well remove the migration and pull the next changeset ids up. if you do this make sure to update #migrations on slack
target-start-of-week (.indexOf ^clojure.lang.PersistentVector days-of-week (setting/get-keyword :start-of-week)) | ||
delta (int (- target-start-of-week db-start-of-week))] | ||
(* (Integer/signum delta) | ||
(- 7 (Math/abs delta))))) |
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.
I might have missed them, but I didn't see tests for this. It's probably worth adding some tests around start-of-week-offset
and the adjust functions. I feel like it'd be easy for an update down the line to introduce an off-by-one error.
{$subtract [column | ||
{$multiply [{$subtract [(day-of-week column) | ||
1]} | ||
(* 24 60 60 1000)]}]}) |
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.
how will this behave during a daylight savings time transition?
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.
I would imagine poorly, but we had this code for a few years now and nobody complained yet. I don't think it's in scope of this PR, I'll open a new issue for it though -- #13344
* Add `db-start-of-week` multimethod * Update drivers & settings [ci drivers] * Use tru in setting def [ci drivers] * Remove reflections [ci drivers] * Fix migration [ci drivers] * Update adjust * Punt on adjust for now * Fix tests [ci drivers] * Fix tests [ci drivers] * Add missing args [ci drivers] * Fix tests [ci all] * Short-circuit if offset = 0 [ci drivers] * Fix week of year test [ci drivers] * h2: use iso-week [ci drivers] * Typo [ci drivers] * Add UI for setting * fix FE assumptions * Unified week-of-year [ci drivers] * Make h2 work regardless of locale [ci drivers] * h2: make `add-interval-honeysql-form` work with expression amounts [ci drivers] * Fix h2 tests [ci all] * Don't rely on int division [ci drivers] * Better week-of-year [ci drivers] * Make linter happy [ci drivers] * More celanup [ci drivers] * Typo [ci drivers] * Fix hive & snowflake [ci drivers] * Add missing require [ci drivers] * Bump hive [ci drivers] * Mongo: add week-of-year [ci all] * Druid: week-of-year [ci drivers] * Fix Mongo [ci drivers] * More fixes [ci drivers] * Fix oracle [ci drivers] * tweak copy and move the setting up * Oracle: use to_char [ci drivers] * More tests [ci drivers] * Remove unneeded migration [ci drivers] * Druid: don't be fancy with woy [ci drivers] * Remove references to `start_of_week` from tests [ci drivers] * Druid: use correct values in test [ci drivers] * More tests [ci drivers] * Typo [ci drivers] * Fix Google driver [ci drivers] Co-authored-by: Paul Rosenzweig <paul.a.rosenzweig@gmail.com> Co-authored-by: Maz Ameli <maz@metabase.com> Co-authored-by: Cam Saul <github@camsaul.com>
Only a few DBs have explicit support for setting start of week (see bellow), but we can active the desired effect using date arithmetics (which we already support) by doing:
where offset is relative to the DB start of week (eg. if start of week is Sunday, then Monday would be +1 and Saturday -1).
To fully support start of week on the BE we therefore need to:
sql.qp/date
for:week
, andday-of-week
to take start of week into account either by performing the calculation above or using a native solution (sql.qp/date
is overridden for every driver anyway so it doesn't make much difference).Open questions
Week of year is tricky. Some DBs use ISO definition of first week of year (week with the year's first Thursday in it), while others use the week that contains January 1st. Do we unify this and if so, what do we use? Any sort of unification will potentially alter results for some users. [I've decided to unify this]
What should be the default start of week? [I'd say keep Sunday for consistency sake while upgrading]
Should we display all 7 days in the dropdown or only Sat, Sun, & Mon?
* What do we do with:day-of-week
date extractor? Currently we normalize this across DBs to ODBC standard (start on Sunday = 1). Should it change in accordance with selected start of week (so that whatever day is start of week has index 1). [My vote: yes]* Where available should we set start of week as a session variable? If so, do we set it upon connection (and when set by user), or do we play it dumb but robust and do it on every query invocation? [My vote: no -- let's just compute it, feels like there's less chance of stumbling into corner cases. Also setting anything would be another instance of us not being read-only.]* Is start of week a per instance, per db, or per user? [I think per-instance is fine, unless we need finer control for EE]* for DBs that have globally settable start of week or follow a system setting, do we sync this setting on each sync? [My vote: yes. Seems harmless enough to do and gives the option of manual override]* do we use DB specific (where available) means of getting the DB start of week, or simply a probing query (see #13090 (comment)) where start of week is not fixed? The latter has the benefit of not being DB specific.DBs with explicit support for start of week
The rest of DBs we support either have a fixed day or you can pick between Sunday and Monday by using different functions.
Things left for a followup PR (to limit scope)
Getting the session start of week setting in the 3 DBs that support this and store it during sync. In the case of Oracle this is tricky, as it's entirely conceivable that people with DBs in multiple territories (eg availability zones) will have different start of week. To do this absolutely right we'd first have to do driver features dispatch per-db (see Move from per-driver features to per-db (have supports? take a database param as well) #13136).
update util.date-2/adjuster