Skip to content
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

SQL: Extend DATE_TRUNC to also operate on intervals(elastic - #46632 ) #47720

Merged
merged 32 commits into from
Mar 23, 2020
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9780340
DateTrunc work with intervals
Oct 8, 2019
23254eb
Revert "DateTrunc work with intervals"
Oct 13, 2019
69e563f
DATE_TRUNC operate with Intervals
Oct 17, 2019
183f210
added integration tests
Oct 29, 2019
579ff66
Update x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/ex…
Oct 30, 2019
ea2eea6
fixed integration tests
Oct 30, 2019
332c9c5
syntax
Oct 30, 2019
2420521
Update x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/ex…
Oct 30, 2019
97739eb
syntax
Oct 30, 2019
4216a7c
fixes
Oct 31, 2019
9741205
enabled dateTruncGroupByWithInterval integration test
Oct 31, 2019
ebad860
fixed docs
Oct 31, 2019
e302f4e
update
Jan 12, 2020
f675bb4
update
Mar 9, 2020
95f5796
update from original elastic
Mar 12, 2020
1f5bd0c
update typeresolutions according to new implementation
Mar 12, 2020
01af08e
update
Mar 12, 2020
3019ed7
Update x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/ex…
Mar 13, 2020
a6deeda
updated integration test cases
Mar 13, 2020
a99555c
syntax problems at DateTrunc and BinaryDateTimeFunction
Mar 13, 2020
c5df03f
syntax
Mar 13, 2020
0d090b5
removed unnecessary changes
Mar 13, 2020
da8b1cf
changed sql exception message for weeks
Mar 13, 2020
c2ddbdf
checked styles
Mar 15, 2020
506d0a7
Update x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/ex…
Mar 16, 2020
c00728c
Update x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/ex…
Mar 16, 2020
e4bef09
Update x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/ex…
Mar 16, 2020
d7a8c13
style
Mar 16, 2020
615fc86
fixed interval ms
Mar 17, 2020
cf004d7
Merge branch 'master' into master
elasticmachine Mar 18, 2020
9ac9839
format
Mar 22, 2020
0b63888
update
Mar 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions docs/reference/sql/functions/date-time.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -500,18 +500,19 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[datePartDateTimeTzOffsetMinus]
--------------------------------------------------
DATE_TRUNC(
string_exp, <1>
datetime_exp) <2>
datetime_exp/interval_exp) <2>
--------------------------------------------------

*Input*:

<1> string expression denoting the unit to which the date/datetime should be truncated to
<2> date/datetime expression
<1> string expression denoting the unit to which the date/datetime/interval should be truncated to
<2> date/datetime/interval expression

*Output*: datetime
*Output*: datetime/interval

*Description*: Truncate the date/datetime to the specified unit by setting all fields that are less significant than the specified
*Description*: Truncate the date/datetime/interval to the specified unit by setting all fields that are less significant than the specified
one to zero (or one, for day, day of week and month). If any of the two arguments is `null` a `null` is returned.
If the first argument is `week` and the second argument is of `interval` type, an error is thrown since the `interval` data type doesn't support a `week` time unit.

[cols="^,^"]
|===
Expand Down Expand Up @@ -563,6 +564,21 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[truncateDateDecades]
include-tagged::{sql-specs}/docs/docs.csv-spec[truncateDateQuarter]
--------------------------------------------------

[source, sql]
--------------------------------------------------
include-tagged::{sql-specs}/docs/docs.csv-spec[truncateIntervalCenturies]
--------------------------------------------------

[source, sql]
--------------------------------------------------
include-tagged::{sql-specs}/docs/docs.csv-spec[truncateIntervalHour]
--------------------------------------------------

[source, sql]
--------------------------------------------------
include-tagged::{sql-specs}/docs/docs.csv-spec[truncateIntervalDay]
--------------------------------------------------

[[sql-functions-datetime-day]]
==== `DAY_OF_MONTH/DOM/DAY`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class DateUtils {
public static final ZoneId UTC = ZoneId.of("Z");

public static final String EMPTY = "";

public static final DateTimeFormatter ISO_DATE_WITH_MILLIS = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(ISO_LOCAL_DATE)
Expand Down Expand Up @@ -72,17 +72,17 @@ public class DateUtils {
.appendOffsetId()
.toFormatter(Locale.ROOT);

private static final int SECONDS_PER_MINUTE = 60;
private static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * 60;
private static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * 24;
public static final int SECONDS_PER_MINUTE = 60;
public static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * 60;
public static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * 24;

private DateUtils() {}

public static String toString(Object value) {
if (value == null) {
return "null";
}

if (value instanceof ZonedDateTime) {
return ((ZonedDateTime) value).format(ISO_DATE_WITH_MILLIS);
}
Expand Down
71 changes: 71 additions & 0 deletions x-pack/plugin/sql/qa/src/main/resources/datetime.csv-spec
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,16 @@ DATE_TRUNC('week', '2019-09-04'::date) as dt_week, DATE_TRUNC('day', '2019-09-0
2000-01-01T00:00:00.000Z | 2000-01-01T00:00:00.000Z | 2010-01-01T00:00:00.000Z | 2019-01-01T00:00:00.000Z | 2019-07-01T00:00:00.000Z | 2019-09-01T00:00:00.000Z | 2019-09-02T00:00:00.000Z | 2019-09-04T00:00:00.000Z
;

selectDateTruncWithInterval
SELECT DATE_TRUNC('hour', INTERVAL '1 12:43:21' DAY TO SECONDS) as dt_hour, DATE_TRUNC('minute', INTERVAL '1 12:43:21' DAY TO SECONDS) as dt_min,
DATE_TRUNC('seconds', INTERVAL '1 12:43:21' DAY TO SECONDS) as dt_sec, DATE_TRUNC('ms', INTERVAL '1 12:43:21' DAY TO SECONDS)::string as dt_millis,
DATE_TRUNC('mcs', INTERVAL '1 12:43:21' DAY TO SECONDS)::string as dt_micro, DATE_TRUNC('nanoseconds', INTERVAL '1 12:43:21' DAY TO SECONDS)::string as dt_nano;

dt_hour | dt_min | dt_sec | dt_millis | dt_micro | dt_nano
---------------+---------------+---------------+---------------+---------------+---------------
+1 12:00:00 |+1 12:43:00 |+1 12:43:21 |+1 12:43:21 |+1 12:43:21 |+1 12:43:21
;

selectDateTruncWithField
schema::emp_no:i|birth_date:ts|dt_mil:ts|dt_cent:ts|dt_dec:ts|dt_year:ts|dt_quarter:ts|dt_month:ts|dt_week:ts|dt_day:ts
SELECT emp_no, birth_date, DATE_TRUNC('millennium', birth_date) as dt_mil, DATE_TRUNC('centuries', birth_date) as dt_cent,
Expand Down Expand Up @@ -585,6 +595,21 @@ SELECT emp_no, hire_date, DATE_TRUNC('quarter', hire_date) as dt FROM test_emp O
10076 | 1985-07-09 00:00:00.000Z | 1985-07-01 00:00:00.000Z
;

dateTruncOrderByWithInterval
schema::first_name:s|dt:ts|hire_date:ts|languages:byte
SELECT first_name, hire_date + DATE_TRUNC('centuries', CASE WHEN languages = 5 THEN INTERVAL '18-3' YEAR TO MONTH
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this query, I'd like to see hire_date and languages returned as well, so that we see the effect of hire_date + DATE_TRUNC.

WHEN languages = 4 THEN INTERVAL '108-4' YEAR TO MONTH WHEN languages = 3 THEN INTERVAL '212-3' YEAR TO MONTH
ELSE INTERVAL '318-6' YEAR TO MONTH END) as dt, hire_date, languages FROM test_emp WHERE emp_no <= 10006 ORDER BY dt NULLS LAST LIMIT 5;

first_name | dt | hire_date | languages
--------------+--------------------------+--------------------------+-----------
Bezalel | 1985-11-21 00:00:00.000Z | 1985-11-21T00:00:00.000Z | 5
Chirstian | 1986-12-01 00:00:00.000Z | 1986-12-01T00:00:00.000Z | 5
Parto | 2086-08-28 00:00:00.000Z | 1986-08-28T00:00:00.000Z | 4
Anneke | 2189-06-02 00:00:00.000Z | 1989-06-02T00:00:00.000Z | 3
Georgi | 2286-06-26 00:00:00.000Z | 1986-06-26T00:00:00.000Z | 2
;

dateTruncFilter
schema::emp_no:i|hire_date:ts|dt:ts
SELECT emp_no, hire_date, DATE_TRUNC('quarter', hire_date) as dt FROM test_emp WHERE DATE_TRUNC('quarter', hire_date) > '1994-07-01T00:00:00.000Z'::timestamp ORDER BY emp_no;
Expand All @@ -601,6 +626,24 @@ SELECT emp_no, hire_date, DATE_TRUNC('quarter', hire_date) as dt FROM test_emp W
10093 | 1996-11-05 00:00:00.000Z | 1996-10-01 00:00:00.000Z
;

dateTruncFilterWithInterval
schema::first_name:s|hire_date:ts
SELECT first_name, hire_date FROM test_emp WHERE hire_date > '2090-03-05T10:11:22.123Z'::datetime - DATE_TRUNC('centuries', INTERVAL 190 YEARS) ORDER BY first_name DESC, hire_date ASC LIMIT 10;

first_name | hire_date
---------------+-------------------------
null | 1990-06-20 00:00:00.000Z
null | 1990-12-05 00:00:00.000Z
null | 1991-09-01 00:00:00.000Z
null | 1992-01-03 00:00:00.000Z
null | 1994-02-17 00:00:00.000Z
Yongqiao | 1995-03-20 00:00:00.000Z
Yishay | 1990-10-20 00:00:00.000Z
Yinghua | 1990-12-25 00:00:00.000Z
Weiyi | 1993-02-14 00:00:00.000Z
Tuval | 1995-12-15 00:00:00.000Z
;

dateTruncGroupBy
schema::count:l|dt:ts
SELECT count(*) as count, DATE_TRUNC('decade', hire_date) dt FROM test_emp GROUP BY dt ORDER BY 2;
Expand All @@ -611,6 +654,24 @@ SELECT count(*) as count, DATE_TRUNC('decade', hire_date) dt FROM test_emp GROUP
41 | 1990-01-01 00:00:00.000Z
;

dateTruncGroupByWithInterval
schema::count:l|dt:ts
SELECT count(*) as count, birth_date + DATE_TRUNC('hour', INTERVAL '1 12:43:21' DAY TO SECONDS) dt FROM test_emp GROUP BY dt ORDER BY 2 LIMIT 10;

count | dt
--------+-------------------------
10 | null
1 | 1952-02-28 12:00:00.000Z
1 | 1952-04-20 12:00:00.000Z
1 | 1952-05-16 12:00:00.000Z
1 | 1952-06-14 12:00:00.000Z
1 | 1952-07-09 12:00:00.000Z
1 | 1952-08-07 12:00:00.000Z
1 | 1952-11-14 12:00:00.000Z
1 | 1952-12-25 12:00:00.000Z
1 | 1953-01-08 12:00:00.000Z
;

dateTruncHaving
schema::gender:s|dt:ts
SELECT gender, max(hire_date) AS dt FROM test_emp GROUP BY gender HAVING DATE_TRUNC('year', max(hire_date)) >= '1997-01-01T00:00:00.000Z'::timestamp ORDER BY 1;
Expand All @@ -621,6 +682,16 @@ null | 1999-04-30 00:00:00.000Z
F | 1997-05-19 00:00:00.000Z
;

// Awaits fix: https://github.com/elastic/elasticsearch/issues/53565
dateTruncHavingWithInterval-Ignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

schema::gender:s|dt:ts
SELECT gender, max(hire_date) AS dt FROM test_emp GROUP BY gender HAVING max(hire_date) - DATE_TRUNC('hour', INTERVAL 1 YEARS) >= '1997-01-01T00:00:00.000Z'::timestamp ORDER BY 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, improve this test so that it returns more than one result. Especially, when order is involved. Returning one result and ordering by it doesn't prove ordering works. Something like this: SELECT gender, max(hire_date) AS dt, COUNT(*) FROM test_emp GROUP BY gender HAVING max(hire_date) - DATE_TRUNC('hour', INTERVAL 1 YEARS) >= '1967-01-01T00:00:00.000Z'::timestamp ORDER BY 2

Copy link
Author

@musteaf musteaf Mar 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@astefan @matriv
I am working on this query.

HAVING max(hire_date) - [ANY INTERVAL VALUE like INTERVAL 1 YEARS]
I am not using even DATE_TRUNC function in the test and I get this error.

Caused by: java.lang.ClassCastException: class org.elasticsearch.xpack.sql.expression.literal.interval.IntervalYearMonth cannot be cast to class java.lang.Number (org.elasticsearch.xpack.sql.expression.literal.interval.IntervalYearMonth is in unnamed module of loader java.net.FactoryURLClassLoader @6a3fbcb1; java.lang.Number is in module java.base of loader 'bootstrap') at org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.SqlBinaryArithmeticOperation.lambda$static$1(SqlBinaryArithmeticOperation.java:60) at org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.SqlBinaryArithmeticOperation.doApply(SqlBinaryArithmeticOperation.java:129) at org.elasticsearch.xpack.ql.expression.predicate.PredicateBiFunction.apply(PredicateBiFunction.java:24) at org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalSqlScriptUtils.sub(InternalSqlScriptUtils.java:142) at org.elasticsearch.painless.PainlessScript$Script.execute(InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.gte(InternalSqlScriptUtils.sub(params.a0,InternalSqlScriptUtils.intervalYearMonth(params.v0,params.v1)),InternalSqlScriptUtils.asDateTime(params.v2))):125) ... 24 more

In my opinion, It is not related to DATE_TRUNC function. I already tried with
SELECT gender, max(hire_date) AS dt, COUNT(*) FROM test_emp GROUP BY gender HAVING max(hire_date) - INTERVAL 1 YEARS >= '1967-01-01T00:00:00.000Z'::timestamp ORDER BY 2
It gives same error.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matriv Most probably, there is a problem with max() function, because it converts max(hire_date) to Number so when I debugged, I saw that It executes

if (l instanceof Number) {
 return Arithmetics.sub((Number) l, (Number) r);
}

in SqlBinaryArithmeticOperation class at SUB enum.
That's why it trying to cast Interval to Number.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@musteaf Hm, yeah, doesn't seem right, maybe something wrong with the data type of max(). Would you mind opening an issue for that?

Copy link
Author

@musteaf musteaf Mar 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matriv Okay, I will open an issue. Also, I think I need to -ignore the test case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes please, and add a comment pointing to the issue, Thx!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @matriv, I added. #53565. Also I want to work on it if it is possible.


gender | dt
--------+-------------------------
null | 1999-04-30 00:00:00.000Z
;

selectDatePartWithDate
SELECT DATE_PART('year', '2019-09-04'::date) as dp_years, DATE_PART('quarter', '2019-09-04'::date) as dp_quarter, DATE_PART('month', '2019-09-04'::date) as dp_month,
DATE_PART('dayofyear', '2019-09-04'::date) as dp_doy, DATE_PART('day', '2019-09-04'::date) as dp_day, DATE_PART('week', '2019-09-04'::date) as dp_week,
Expand Down
30 changes: 30 additions & 0 deletions x-pack/plugin/sql/qa/src/main/resources/docs/docs.csv-spec
Original file line number Diff line number Diff line change
Expand Up @@ -2675,6 +2675,36 @@ SELECT DATETRUNC('quarters', CAST('2019-09-04' AS DATE)) AS quarter;
// end::truncateDateQuarter
;

truncateIntervalCenturies
// tag::truncateIntervalCenturies
SELECT DATE_TRUNC('centuries', INTERVAL '199-5' YEAR TO MONTH) AS centuries;

centuries
------------------
+100-0
// end::truncateIntervalCenturies
;

truncateIntervalHour
// tag::truncateIntervalHour
SELECT DATE_TRUNC('hours', INTERVAL '17 22:13:12' DAY TO SECONDS) AS hour;

hour
------------------
+17 22:00:00
// end::truncateIntervalHour
;

truncateIntervalDay
// tag::truncateIntervalDay
SELECT DATE_TRUNC('days', INTERVAL '19 15:24:19' DAY TO SECONDS) AS day;

day
------------------
+19 00:00:00
// end::truncateIntervalDay
;

constantDayOfWeek
// tag::dayOfWeek
SELECT DAY_OF_WEEK(CAST('2018-02-19T10:23:27Z' AS TIMESTAMP)) AS day;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
public final class SqlTypeResolutions {

private SqlTypeResolutions() {}

public static TypeResolution isDate(Expression e, String operationName, ParamOrdinal paramOrd) {
return isType(e, SqlDataTypes::isDateBased, operationName, paramOrd, "date", "datetime");
}
Expand All @@ -25,6 +25,10 @@ public static TypeResolution isDateOrTime(Expression e, String operationName, Pa
return isType(e, SqlDataTypes::isDateOrTimeBased, operationName, paramOrd, "date", "time", "datetime");
}

public static TypeResolution isDateOrInterval(Expression e, String operationName, ParamOrdinal paramOrd) {
return isType(e, SqlDataTypes::isDateOrIntervalBased, operationName, paramOrd, "date", "datetime", "an interval data type");
}

public static TypeResolution isNumericOrDate(Expression e, String operationName, ParamOrdinal paramOrd) {
return isType(e, dt -> dt.isNumeric() || SqlDataTypes.isDateBased(dt), operationName, paramOrd,
"date", "datetime", "numeric");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isString;
import static org.elasticsearch.xpack.ql.expression.gen.script.ParamsBuilder.paramsBuilder;
import static org.elasticsearch.xpack.sql.expression.SqlTypeResolutions.isDate;

public abstract class BinaryDateTimeFunction extends BinaryScalarFunction {

Expand Down Expand Up @@ -54,10 +53,7 @@ protected TypeResolution resolveType() {
}
}
}
resolution = isDate(right(), sourceText(), Expressions.ParamOrdinal.SECOND);
if (resolution.unresolved()) {
return resolution;
}

return TypeResolution.TYPE_RESOLVED;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;

import org.elasticsearch.xpack.ql.expression.Expression;
import org.elasticsearch.xpack.ql.expression.Expressions;
import org.elasticsearch.xpack.ql.expression.function.scalar.BinaryScalarFunction;
import org.elasticsearch.xpack.ql.expression.gen.pipeline.Pipe;
import org.elasticsearch.xpack.ql.tree.NodeInfo;
Expand All @@ -23,6 +24,8 @@
import java.util.Set;
import java.util.function.ToIntFunction;

import static org.elasticsearch.xpack.sql.expression.SqlTypeResolutions.isDate;

public class DatePart extends BinaryDateTimeFunction {

public enum Part implements DateTimeField {
Expand Down Expand Up @@ -84,6 +87,19 @@ public DataType dataType() {
return DataTypes.INTEGER;
}

@Override
protected TypeResolution resolveType() {
TypeResolution resolution = super.resolveType();
if (resolution.unresolved()) {
return resolution;
}
resolution = isDate(right(), sourceText(), Expressions.ParamOrdinal.SECOND);
if (resolution.unresolved()) {
return resolution;
}
return TypeResolution.TYPE_RESOLVED;
}

@Override
protected BinaryScalarFunction replaceChildren(Expression newDateTimePart, Expression newTimestamp) {
return new DatePart(source(), newDateTimePart, newTimestamp, zoneId());
Expand Down
Loading