-
Notifications
You must be signed in to change notification settings - Fork 0
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
Add maketime
and makedate
#102
Conversation
Signed-off-by: Yury Fridlyand <yuryf@bitquilltech.com>
Signed-off-by: Yury Fridlyand <yuryf@bitquilltech.com>
Codecov Report
@@ Coverage Diff @@
## integ-make-time-n-date #102 +/- ##
============================================================
- Coverage 97.74% 94.84% -2.90%
- Complexity 2858 2908 +50
============================================================
Files 273 287 +14
Lines 7020 7825 +805
Branches 442 571 +129
============================================================
+ Hits 6862 7422 +560
- Misses 157 349 +192
- Partials 1 54 +53
Flags with carried forward coverage won't be shown. Click here to find out more.
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
Signed-off-by: Yury Fridlyand <yuryf@bitquilltech.com>
} | ||
|
||
private ExprValue exprMakeTime(ExprValue hour, ExprValue minute, ExprValue second) { | ||
if (0 > hour.doubleValue() || 0 > minute.doubleValue() || 0 > second.doubleValue()) { |
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.
Can the minutes and seconds not be valid at 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.
I check here for negative values, zero is OK for maketime
:
opensearchsql> select maketime(0, 0, 0);
fetched rows / total rows = 1/1
+---------------------+
| maketime(0, 0, 0) |
|---------------------|
| 00:00:00 |
+---------------------+
reference:
mysql> select maketime(0, 0, 0);
+-------------------+
| maketime(0, 0, 0) |
+-------------------+
| 00:00:00 |
+-------------------+
1 row in set (0.00 sec)
var r = new Random(); | ||
assertEquals(maketime(20., 30., 40.), LocalTime.of(20, 30, 40)); | ||
|
||
for (var ignored : IntStream.range(0, 25).toArray()) { |
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.
Why is this for loop necessary? What does it test that isn't covered by the test on line 1024 above?
As an aside, tests must be reproducible so if a test uses RNG, the user must be able to seed it and any test failures must include the seed used. Otherwise, if it does fail, there is no way to investigate the failure.
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.
Instead of testing RNG numbers, test boundary cases. For example: 0, and 23:59:59.99 would be decent test cases for time.
Feb 29 on leap years, and older years would be good boundary cases for dates.
Lots of timezones (and over the the dateline) are also excellent test cases when timezones are included.
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 dump values used on case of failure, but if it not enough I can hardcode values gotten from RNG.
* @param exprToFill Expression to fill the rest place, usually valid one | ||
* @return A collection with all possible permutations | ||
*/ | ||
private Set<Expression[]> permuteArgumentsWith(int size, |
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 tests would be easier to understand if the permutations tested were listed.
Since this function is only used with size
of 2 and 3, I'd replace it with a function that just uses List.of(...)
and lists all the permutations.
"Negative year doesn't produce NULL"); | ||
assertEquals(nullValue(), eval(makedate(DSL.literal(42.), DSL.literal(-1.))), | ||
"Negative dayOfYear doesn't produce NULL"); | ||
assertEquals(nullValue(), eval(makedate(DSL.literal(42.), DSL.literal(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.
What about day 366 of a non-leap year?
@Test | ||
public void makedate() { | ||
var r = new Random(); | ||
for (var ignored : IntStream.range(0, 1025).toArray()) { |
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.
What cases are tested in this for loop? Why is RNG necessary?
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.
RNG helps to find unconsidered cases, it helped me to find few bugs. I dump values which cause a failure, so any other developer would be able to reproduce it.
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.
A good discussion on using RNG in unit/integration tests: https://softwareengineering.stackexchange.com/questions/429601/is-the-usage-of-random-values-in-unit-testing-a-good-practice
>>>>>>>>>>> | ||
|
||
Returns a date, given `year` and `day-of-year` values. `dayofyear` must be greater than 0 or the result is `NULL`. The result is also `NULL` if either argument is `NULL`. | ||
Arguments are rounded to an integer. |
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.
Was there a test case for this rounding behaviour in DateTimeFunctionTest?
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.
What about invalid positive numbers? such as dayofyear 999
?
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.
Was there a test case for this rounding behaviour in DateTimeFunctionTest?
Thanks, I will add.
What about invalid positive numbers? such as dayofyear
999
?
There is no limit! Freedom!
docs/user/dql/functions.rst
Outdated
+------------+------------+ | ||
| f1 | f2 | | ||
|------------+------------| | ||
| 1945-01-06 | 1989-06-06 | |
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 sample code is going to show that MAKEDATE(1984, 1984) is 1989-...
the description should describe this behaviour as well.
This is also a good test case to have in the unit tests -- that MAKEDATE($year, $days_in_year + 1) == MAKEDATE($year + 1, 1)
.
MAKETIME | ||
-------- | ||
|
||
Description | ||
>>>>>>>>>>> | ||
|
||
Returns a time value calculated from the hour, minute, and second arguments. Returns `NULL` if any of its arguments are `NULL`. | ||
The second argument can have a fractional part, rest arguments are rounded to an integer. |
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 the fractional part rounded to a particular number of decimal places?
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.
What about hour > 24? What about minute/second > 60?
Make sure to indicate 24 hour time is used, not 12-hour with am/pm.
@@ -388,15 +388,60 @@ Example:: | |||
+--------------------------+ | |||
|
|||
|
|||
MAKEDATE |
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.
Same comments as in SQL apply here.
@@ -994,6 +1006,139 @@ public void date_format() { | |||
assertEquals(missingValue(), eval(dsl.date_format(DSL.literal(""), missingRef))); | |||
} | |||
|
|||
private FunctionExpression maketime(Expression hour, Expression minute, Expression 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.
I realize these tests follow the convention in this file, but the tests would be much easier to understand if each function had it's own test suite, and each test in a suite tested a particular scenario -- all valid arguments, null as an argument, negative values, rounding behaviour, etc.
I'll leave it up to you whether you want to break with the convention and introduce a MakeTimeTests.
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 a good suggestion. I would recommend setting the convention for the test file to include the original name, such as: DateTimeFunctionMakedateTest
and DateTimeFunctionMaketimeTest
. It would be easier to find for future developers.
core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java
Show resolved
Hide resolved
core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java
Show resolved
Hide resolved
@@ -994,6 +1006,139 @@ public void date_format() { | |||
assertEquals(missingValue(), eval(dsl.date_format(DSL.literal(""), missingRef))); | |||
} | |||
|
|||
private FunctionExpression maketime(Expression hour, Expression minute, Expression 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.
This is a good suggestion. I would recommend setting the convention for the test file to include the original name, such as: DateTimeFunctionMakedateTest
and DateTimeFunctionMaketimeTest
. It would be easier to find for future developers.
var r = new Random(); | ||
assertEquals(maketime(20., 30., 40.), LocalTime.of(20, 30, 40)); | ||
|
||
for (var ignored : IntStream.range(0, 25).toArray()) { |
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.
Instead of testing RNG numbers, test boundary cases. For example: 0, and 23:59:59.99 would be decent test cases for time.
Feb 29 on leap years, and older years would be good boundary cases for dates.
Lots of timezones (and over the the dateline) are also excellent test cases when timezones are included.
>>>>>>>>>>> | ||
|
||
Returns a date, given `year` and `day-of-year` values. `dayofyear` must be greater than 0 or the result is `NULL`. The result is also `NULL` if either argument is `NULL`. | ||
Arguments are rounded to an integer. |
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.
What about invalid positive numbers? such as dayofyear 999
?
assertEquals(nullValue(), eval(makedate(DSL.literal(42.), DSL.literal(-1.))), | ||
"Negative dayOfYear doesn't produce NULL"); | ||
assertEquals(nullValue(), eval(makedate(DSL.literal(42.), DSL.literal(0.))), | ||
"Zero dayOfYear doesn't produce NULL"); |
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.
what about tests for invalid hour/minute/second or year/month/day?
(is there an invalid year, or is year 0 and 9999 valid?)
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.
MySQL is limited on output value: from 0001-01-01 to 9999-12-31. Java has no limits.
MAKETIME | ||
-------- | ||
|
||
Description | ||
>>>>>>>>>>> | ||
|
||
Returns a time value calculated from the hour, minute, and second arguments. Returns `NULL` if any of its arguments are `NULL`. | ||
The second argument can have a fractional part, rest arguments are rounded to an integer. |
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.
What about hour > 24? What about minute/second > 60?
Make sure to indicate 24 hour time is used, not 12-hour with am/pm.
Signed-off-by: Yury Fridlyand <yuryf@bitquilltech.com>
Signed-off-by: Yury Fridlyand <yuryf@bitquilltech.com>
@@ -512,6 +528,28 @@ private ExprValue exprHour(ExprValue time) { | |||
return new ExprIntegerValue(time.timeValue().getHour()); | |||
} | |||
|
|||
private ExprValue exprMakeDate(ExprValue yearExpr, ExprValue dayOfYearExp) { |
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.
A javadoc with some explanation of the mysql compliance would be nice.
Signed-off-by: Yury Fridlyand <yuryf@bitquilltech.com>
Signed-off-by: Yury Fridlyand <yuryf@bitquilltech.com>
Signed-off-by: Yury Fridlyand <yuryf@bitquilltech.com>
var hour = Math.round(hourExpr.doubleValue()); | ||
var minute = Math.round(minuteExpr.doubleValue()); | ||
var second = secondExpr.doubleValue(); | ||
if (0 > hour || 0 > minute || 0 > 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.
nit: Can we make this check prior to rounding? Then if the value is negative, we save those operations.
var year = Math.round(yearExpr.doubleValue()); | ||
var dayOfYear = Math.round(dayOfYearExp.doubleValue()); | ||
// We need to do this to comply with MySQL | ||
if (0 >= dayOfYear || 0 > year) { |
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.
nit: Can we make this check prior to rounding? Then if the value is negative, we save those operations.
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.
To optimize performance we shouldn't use LocalDate::parse
nor LocalDate::plusDays
. See my note about faster and easier implementation.
Signed-off-by: Yury Fridlyand <yuryf@bitquilltech.com>
…attern_CI Include feature branch in workflow to trigger CI
Description
maketime
link link00:00:00.0 - 23:59:59.(9)
;makedate
link linkFunctions have 2 implementations:
I used
easier
one, butfaster
one used in unit tests. Could be swapped.According to MySQL both functions accept double values. Arguments which are not supposed to have fraction parts (e.g. hour, year) are rounded.
TODO
Check List
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.