-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
Simplify predicates involving year function #16106
Conversation
Why would this rewrite improve performance? |
@martint We have |
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's a roundabout way of improving this specific type of expression. The optimization should go the other way -- an expression like year(date_time) = t
is inherently cheaper than date_trunc('year', date_time) = ...
, since the date_trunc
function is more generic and expensive to compute. In general, optimizations should stand on their own (i.e., not depend on other rules to be successful). They should produce a better plan regardless of whether another optimization kicks in later.
1b64348
to
b9bb1ac
Compare
year
function to date_trunc
for improving performanceb9bb1ac
to
38338cb
Compare
@martint Changed the implementation not to depend on |
} | ||
if (type instanceof TimestampType timestampType) { | ||
if (timestampType.isShort()) { | ||
LocalDateTime dateTime = LocalDateTime.MAX.withYear(year).with(lastDayOfYear()); |
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.
Is .with(lastDayOfYear())
still necessary in the context of using LocalDateTime.MAX
?
} | ||
if (type instanceof TimestampType timestampType) { | ||
if (timestampType.isShort()) { | ||
LocalDateTime dateTime = LocalDateTime.of(year, 1, 1, 0, 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.
it's same for short and long timestamp type, so move before the if (timestampType.isShort())
check:
long yearStartEpochSecond = LocalDateTime.of(year, 1, 1, 0, 0).toEpochSecond(ZoneOffset.UTC);
long yearStartEpochMicros = multiplyExact(yearStartEpochSecond, MICROSECONDS_PER_SECOND);
if (timestampType.isShort()) {
return yearStartEpochMicros;
}
return new LongTimestamp(yearStartEpochMicros, 0);
if (type instanceof TimestampType timestampType) { | ||
if (timestampType.isShort()) { | ||
LocalDateTime dateTime = LocalDateTime.of(year, 1, 1, 0, 0); | ||
return dateTime.toEpochSecond(ZoneOffset.UTC) * MICROSECONDS_PER_SECOND + LongMath.divide(dateTime.getNano(), NANOSECONDS_PER_MICROSECOND, UNNECESSARY); |
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.
dateTime.getNano()
is known to be 0
if (type instanceof TimestampType timestampType) { | ||
if (timestampType.isShort()) { | ||
LocalDateTime dateTime = LocalDateTime.of(year, 1, 1, 0, 0); | ||
return dateTime.toEpochSecond(ZoneOffset.UTC) * MICROSECONDS_PER_SECOND + LongMath.divide(dateTime.getNano(), NANOSECONDS_PER_MICROSECOND, UNNECESSARY); |
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.
we probably should use multiplyExact
return dateTime.toEpochSecond(ZoneOffset.UTC) * MICROSECONDS_PER_SECOND + LongMath.divide(dateTime.getNano(), NANOSECONDS_PER_MICROSECOND, UNNECESSARY); | ||
} | ||
LocalDateTime dateTime = LocalDateTime.of(year, 1, 1, 0, 0); | ||
long endInclusiveMicros = dateTime.toEpochSecond(ZoneOffset.UTC) * MICROSECONDS_PER_SECOND |
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.
endInclusiveMicros
wrong var name?
} | ||
if (type instanceof TimestampType timestampType) { | ||
if (timestampType.isShort()) { | ||
LocalDateTime dateTime = LocalDateTime.MAX.withYear(year).with(lastDayOfYear()); |
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.
Personally i find LocalDateTime.MAX
harder to reason about.
I first need to assume the max value has .999999999
second fraction (which is obvious if you know LocalDateTime internal representation) and then I need to reason how this behaves for our timestamp with precision > 9.
What about writing this more explicitly
long nextYearStartEpochSecond = LocalDateTime.of(year + 1, 1, 1, 0, 0).toEpochSecond(ZoneOffset.UTC);
long nextYearStartEpochMicros = multiplyExact(nextYearStartEpochSecond, MICROSECONDS_PER_SECOND);
if (timestampType.isShort()) {
// TODO might be off by one
return nextYearStartEpochMicros - scaleFactor(timestampType.getPrecision(), 6);
}
int picosOfMicro = toIntExact(PICOSECONDS_PER_MICROSECOND - scaleFactor(timestampType.getPrecision(), 12));
return new LongTimestamp(nextYearStartEpochMicros - 1, picosOfMicro);
(i hope i didn't screw the math, but not sure, hence the TODO comment; please remove the TODO)
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.
Maybe this method should be unit-tested
{ | ||
checkArgument(constant.getValue() != null && argumentType.equals(TIMESTAMP_TZ_MICROS), "Unexpected constant: %s", constant); | ||
|
||
// Normalized to UTC because for comparisons the zone is irrelevant |
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.
remove (copy&paste error)
|
||
private static Optional<Domain> unwrapYearInComparison(FunctionName functionName, Type argumentType, Constant constant) | ||
{ | ||
checkArgument(constant.getValue() != null && argumentType.equals(TIMESTAMP_TZ_MICROS), "Unexpected constant: %s", constant); |
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.
we check two arguments but report only one in the message
checkArgument(constant.getValue() != null, "Unexpected constant: %s", constant);
checkArgument(type.equals(TIMESTAMP_TZ_MICROS), "Unexpected type: %s", 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.
( #16594 for where this was copied from )
public void testExtractYearTimestampTzComparison() | ||
{ | ||
String timestampTzColumnSymbol = "timestamp_tz_symbol"; | ||
FunctionCall truncateToYear = new FunctionCall( |
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.
extractYear
List.of(new SymbolReference(timestampTzColumnSymbol))); | ||
|
||
LocalDate someDate = LocalDate.of(2005, 9, 10); | ||
Expression someMidnightExpression = LITERAL_ENCODER.toExpression( |
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.
yearExpression
?
long startOfDateUtcEpochMillis = someDate.withDayOfYear(1).atStartOfDay().toEpochSecond(UTC) * MILLISECONDS_PER_SECOND; | ||
LongTimestampWithTimeZone startOfDateUtc = timestampTzFromEpochMillis(startOfDateUtcEpochMillis); | ||
LongTimestampWithTimeZone startOfNextDateUtc = timestampTzFromEpochMillis(someDate.plusYears(1).withDayOfYear(1).atStartOfDay().toEpochSecond(UTC) * MILLISECONDS_PER_SECOND); |
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.
startOfDate -> startOfYear
38338cb
to
7d5a3b5
Compare
Description
Simplify predicates involving
year
function likesUnwrapDateTruncInComparison
.Continuation of #15306
Fixes #14078
Release notes
(x) Release notes are required, with the following suggested text: