SQL: Fix constant folding of datetime functions (elastic/x-pack-elasticsearch#3637)

I went to write some docs for datetime functions that look like:
```
SELECT YEAR(CAST('2018-01-19T10:23:27Z' AS TIMESTAMP)) as year;

  year
2018

```
because I figured they'd be pretty easy to read because they didn't
require any knowledge of a data set. But it turns out that constant
folding doesn't work properly for date time functions because they don't
actually apply the extraction.

Original commit: elastic/x-pack-elasticsearch@aa9c66b2c7
This commit is contained in:
Nik Everett 2018-01-19 15:11:37 -05:00 committed by GitHub
parent f69a09ce83
commit b165f1c71e
4 changed files with 84 additions and 35 deletions

View File

@ -251,8 +251,16 @@ include-tagged::{sql-specs}/math.sql-spec[sinh]
include-tagged::{sql-specs}/math.sql-spec[cosh]
--------------------------------------------------
// conversion
// date time
=== Date and Time Functions
* Extract the year from a date (`YEAR`)
["source","sql",subs="attributes,callouts,macros"]
--------------------------------------------------
include-tagged::{sql-specs}/datetime.csv-spec[year]
--------------------------------------------------
// aggregate
// geospatial

View File

@ -22,6 +22,9 @@ import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.type.DataTypes;
import org.joda.time.DateTimeZone;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoField;
import java.util.Objects;
@ -61,7 +64,13 @@ public abstract class DateTimeFunction extends UnaryScalarFunction {
@Override
public Object fold() {
return field().fold();
Long folded = (Long) field().fold();
if (folded == null) {
return null;
}
ZonedDateTime time = ZonedDateTime.ofInstant(
Instant.ofEpochMilli(folded), ZoneId.of(timeZone.getID()));
return time.get(chronoField());
}
@Override

View File

@ -15,7 +15,13 @@ import org.elasticsearch.xpack.sql.expression.Order;
import org.elasticsearch.xpack.sql.expression.Order.OrderDirection;
import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunction;
import org.elasticsearch.xpack.sql.expression.function.aggregate.Count;
import org.elasticsearch.xpack.sql.expression.function.scalar.Cast;
import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.Add;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfMonth;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfYear;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.MonthOfYear;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.WeekOfWeekYear;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.Year;
import org.elasticsearch.xpack.sql.expression.predicate.And;
import org.elasticsearch.xpack.sql.expression.predicate.Equals;
import org.elasticsearch.xpack.sql.expression.predicate.GreaterThan;
@ -49,7 +55,7 @@ import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.type.DataTypes;
import org.joda.time.DateTimeZone;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -257,6 +263,23 @@ public class OptimizerTests extends ESTestCase {
new ConstantFolding().rule(new RLike(EMPTY, Literal.of(EMPTY, "test_emp"), Literal.of(EMPTY, "test.emp"))));
}
public void testConstantFoldingDatetime() {
Expression cast = new Cast(EMPTY, Literal.of(EMPTY, "2018-01-19T10:23:27Z"), DataTypes.DATE);
assertEquals(2018, unwrapAlias(new ConstantFolding().rule(new Year(EMPTY, cast, DateTimeZone.UTC))));
assertEquals(1, unwrapAlias(new ConstantFolding().rule(new MonthOfYear(EMPTY, cast, DateTimeZone.UTC))));
assertEquals(19, unwrapAlias(new ConstantFolding().rule(new DayOfMonth(EMPTY, cast, DateTimeZone.UTC))));
assertEquals(19, unwrapAlias(new ConstantFolding().rule(new DayOfYear(EMPTY, cast, DateTimeZone.UTC))));
assertEquals(3, unwrapAlias(new ConstantFolding().rule(new WeekOfWeekYear(EMPTY, cast, DateTimeZone.UTC))));
assertNull(unwrapAlias(new ConstantFolding().rule(
new WeekOfWeekYear(EMPTY, new Literal(EMPTY, null, DataTypes.NULL), DateTimeZone.UTC))));
}
private Object unwrapAlias(Expression e) {
Alias a = (Alias) e;
Literal l = (Literal) a.child();
return l.value();
}
public void testBinaryComparisonSimplification() {
assertEquals(Literal.TRUE, new BinaryComparisonSimplification().rule(new Equals(EMPTY, L(5), L(5))));
assertEquals(Literal.TRUE, new BinaryComparisonSimplification().rule(new GreaterThanOrEqual(EMPTY, L(5), L(5))));

View File

@ -10,53 +10,53 @@
dateTimeSecond
SELECT SECOND(birth_date) d, last_name l FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
d:i | l:s
0 | Facello
0 | Simmel
0 | Bamford
0 | Koblick
0 | Maliniak
0 | Preusig
0 | Zielinski
0 | Kalloufi
0 | Peac
d:i | l:s
0 | Facello
0 | Simmel
0 | Bamford
0 | Koblick
0 | Maliniak
0 | Preusig
0 | Zielinski
0 | Kalloufi
0 | Peac
;
dateTimeMinute
SELECT MINUTE(birth_date) d, last_name l FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
d:i | l:s
0 | Facello
0 | Simmel
0 | Bamford
0 | Koblick
0 | Maliniak
0 | Preusig
0 | Zielinski
0 | Kalloufi
0 | Peac
d:i | l:s
0 | Facello
0 | Simmel
0 | Bamford
0 | Koblick
0 | Maliniak
0 | Preusig
0 | Zielinski
0 | Kalloufi
0 | Peac
;
dateTimeHour
SELECT HOUR(birth_date) d, last_name l FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
d:i | l:s
0 | Facello
0 | Simmel
0 | Bamford
0 | Koblick
0 | Maliniak
0 | Preusig
0 | Zielinski
0 | Kalloufi
0 | Peac
d:i | l:s
0 | Facello
0 | Simmel
0 | Bamford
0 | Koblick
0 | Maliniak
0 | Preusig
0 | Zielinski
0 | Kalloufi
0 | Peac
;
//
// Date (in H2 these start at 0 instead of 1...)
//
dateTimeDayOfWeek
dateTimeDayOfWeek
SELECT DAY_OF_WEEK(birth_date) d, last_name l FROM "test_emp" WHERE emp_no < 10010 ORDER BY DAY_OF_WEEK(birth_date);
d:i | l:s
@ -163,3 +163,12 @@ d:i | c:l | s:i
2 | 4 | 40081
1 | 5 | 50167
;
constantYear
// tag::year
SELECT YEAR(CAST('2018-01-19T10:23:27Z' AS TIMESTAMP)) as year;
year
2018
// end::year
;