-
Notifications
You must be signed in to change notification settings - Fork 385
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
feat(spanner): add a representation for the Spanner INTERVAL #14059
Conversation
Add a C++ type, `spanner::Interval`, to model the Spanner `INTERVAL`: the difference between two date/time values. Basic operations and string conversions are provided. Extra operations involving `Date` and `Timestamp` values are upcoming. Integration of `Interval` with `spanner::Value` awaits the publication of the `google.spanner.v1.TypeCode::INTERVAL` enum value.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #14059 +/- ##
==========================================
- Coverage 93.76% 93.23% -0.54%
==========================================
Files 2281 2197 -84
Lines 198737 188653 -10084
==========================================
- Hits 186353 175883 -10470
- Misses 12384 12770 +386 ☔ View full report in Codecov by Sentry. |
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 think the Parser class needs to go, the rest of my comments I could be convinced they are misguided.
google/cloud/spanner/interval.cc
Outdated
std::function<Interval(std::int32_t)> factory; | ||
} const kUnitFactories[] = { | ||
// "days" is first so that it is chosen when unit is empty. | ||
{"days", [](auto n) { return Interval(0, 0, n); }}, |
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 went into a rabbit hole: PostgresQL only supports english to define intervals, I was worried that i18n would force us to parse intervals defined in French or worse.
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.
Sacre bleu!
std::int32_t months_; | ||
std::int32_t days_; | ||
absl::Duration offset_; |
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.
FWIW, this is the documented representation in Postgres:
Can we find any documentation to reference justifying why this is a good representation for Spanner too?
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 most definitive statement I can find internally is "The GoogleSQL interval value is proposed to be logically encoded as an object with 3 fields: 1) # of months, 2) # of days, and 3) # of nanoseconds." However, the whole gist of the design docs is that the same code can handle both GoogleSQL and PostgreSQL dialects.
* A representation of the Spanner INTERVAL type: The difference between | ||
* two date/time values. | ||
* | ||
* @see https://cloud.google.com/spanner/docs/data-types#interval_type |
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 link does not work for me, not yet. The closest I can find is:
https://cloud.google.com/spanner/docs/reference/postgresql/data-types#unsupported-data-types
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.
Yeah, not yet. Internal indications are that the link I gave will be correct when the service support is released. So our alternatives now are to make that assumption (which I chose), or to remove the link, to be re-added later. I'm happy to do the latter if you feel strongly about it.
google/cloud/spanner/interval.cc
Outdated
// Fractional results only flow down into smaller units. Nothing ever | ||
// carries up into larger units. This means that '1 month' / 2 becomes | ||
// '15 days, but '1 month 15 days' * 3 is '3 months 45 days'. |
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.
Okay, but why?
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.
Only because that is how the operation is defined (to the best of my understanding). The important takeaway is that the months/days/duration pieces should be kept distinct (seeing as they don't really inter-convert), until you have no choice. For example, addition and negation are field-by-field, but the alternative here would be that "1 month" / 2
is zero, and I guess that was deemed worse.
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 there is a go/ link or something, maybe add that as a comment. Otherwise, SGTM.
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.
That particular example, albeit written as "1 month" * 0.5
, comes from https://gist.github.com/henryivesjones/ebd653acbf61cb408380a49659e2be97, which is cited near the test of that case.
Internally there is a "1 day" / 2
-> "12 hours"
example in go/spangres-interval-design, but I hesitate to mention that in the code.
google/cloud/spanner/interval.cc
Outdated
StatusOr<Interval> Parser::Parse() { | ||
Interval intvl; | ||
auto s = absl::string_view(s_); |
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.
Do you even need the Parser
class? This is the only place you use s_
, and it seems you could just say:
StatusOr<Interval> ParseInterval(std::string s_) {
Interval intvl;
auto s = absl::string_view(s_);
...
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.
Parser
had additional data members before. But even after simplification to this state, I kept the class as an intra-file visibility barrier (the helpers being private). But I acknowledge that there is disagreement about such tactics, so I'm happy to remove. Done.
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.
Thanks.
I think the Parser class needs to go
Done.
the rest of my comments I could be convinced they are misguided.
Some heeded; some countered.
google/cloud/spanner/interval.cc
Outdated
std::function<Interval(std::int32_t)> factory; | ||
} const kUnitFactories[] = { | ||
// "days" is first so that it is chosen when unit is empty. | ||
{"days", [](auto n) { return Interval(0, 0, n); }}, |
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.
Sacre bleu!
google/cloud/spanner/interval.cc
Outdated
StatusOr<Interval> Parser::Parse() { | ||
Interval intvl; | ||
auto s = absl::string_view(s_); |
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.
Parser
had additional data members before. But even after simplification to this state, I kept the class as an intra-file visibility barrier (the helpers being private). But I acknowledge that there is disagreement about such tactics, so I'm happy to remove. Done.
google/cloud/spanner/interval.cc
Outdated
// Fractional results only flow down into smaller units. Nothing ever | ||
// carries up into larger units. This means that '1 month' / 2 becomes | ||
// '15 days, but '1 month 15 days' * 3 is '3 months 45 days'. |
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.
Only because that is how the operation is defined (to the best of my understanding). The important takeaway is that the months/days/duration pieces should be kept distinct (seeing as they don't really inter-convert), until you have no choice. For example, addition and negation are field-by-field, but the alternative here would be that "1 month" / 2
is zero, and I guess that was deemed worse.
* A representation of the Spanner INTERVAL type: The difference between | ||
* two date/time values. | ||
* | ||
* @see https://cloud.google.com/spanner/docs/data-types#interval_type |
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.
Yeah, not yet. Internal indications are that the link I gave will be correct when the service support is released. So our alternatives now are to make that assumption (which I chose), or to remove the link, to be re-added later. I'm happy to do the latter if you feel strongly about it.
std::int32_t months_; | ||
std::int32_t days_; | ||
absl::Duration offset_; |
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 most definitive statement I can find internally is "The GoogleSQL interval value is proposed to be logically encoded as an object with 3 fields: 1) # of months, 2) # of days, and 3) # of nanoseconds." However, the whole gist of the design docs is that the same code can handle both GoogleSQL and PostgreSQL dialects.
google/cloud/spanner/interval.cc
Outdated
// Fractional results only flow down into smaller units. Nothing ever | ||
// carries up into larger units. This means that '1 month' / 2 becomes | ||
// '15 days, but '1 month 15 days' * 3 is '3 months 45 days'. |
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 there is a go/ link or something, maybe add that as a comment. Otherwise, SGTM.
An update to the `Interval` API introduced in googleapis#14059. Use `std::chrono::nanoseconds` instead of `absl::Duration` to represent the absolute "offset" portion of the interval.
An update to the `Interval` API introduced in #14059. Use `std::chrono::nanoseconds` instead of `absl::Duration` to represent the absolute "offset" portion of the interval.
An update to the `Interval` API introduced in googleapis#14059. Use `std::chrono::nanoseconds` instead of `absl::Duration` to represent the absolute "offset" portion of the interval.
Add a C++ type,
spanner::Interval
, to model the SpannerINTERVAL
: the difference between two date/time values.Basic operations and string conversions are provided. Extra operations involving
Date
andTimestamp
values are upcoming.Integration of
Interval
withspanner::Value
awaits the publication of thegoogle.spanner.v1.TypeCode::INTERVAL
enum value.This change is