Skip to content

SQL: Adds MONTHNAME, DAYNAME and QUARTER functions #33411

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

Merged
merged 9 commits into from
Sep 11, 2018
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@
import org.elasticsearch.xpack.sql.expression.function.aggregate.SumOfSquares;
import org.elasticsearch.xpack.sql.expression.function.aggregate.VarPop;
import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.Mod;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayName;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfMonth;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfWeek;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfYear;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.HourOfDay;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.MinuteOfDay;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.MinuteOfHour;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.MonthName;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.MonthOfYear;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.Quarter;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.SecondOfMinute;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.WeekOfYear;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.Year;
Expand Down Expand Up @@ -62,21 +65,21 @@
import org.elasticsearch.xpack.sql.expression.function.scalar.string.BitLength;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Char;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.CharLength;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.LCase;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.LTrim;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Length;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.RTrim;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Space;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.UCase;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Concat;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Insert;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.LCase;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.LTrim;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Left;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Length;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Locate;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Position;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.RTrim;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Repeat;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Replace;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Right;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Space;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Substring;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.UCase;
import org.elasticsearch.xpack.sql.parser.ParsingException;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.util.StringUtils;
Expand Down Expand Up @@ -123,6 +126,9 @@ public class FunctionRegistry {
def(MonthOfYear.class, MonthOfYear::new, "MONTH"),
def(Year.class, Year::new),
def(WeekOfYear.class, WeekOfYear::new, "WEEK"),
def(DayName.class, DayName::new, "DAYNAME"),
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need alias here and next line?

Copy link
Contributor

Choose a reason for hiding this comment

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

ah, ignore this, Checked already. It's because DayName becomes day_name.

Copy link
Member

Choose a reason for hiding this comment

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

Should we make the class Dayname instead then? Do we want to support DAY_NAME as well as DAYNAME?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nik9000 I chose it like this because of consistency. There are other date/time functions that have aliases, even though the spec mentions only one name. For example WEEK (WEEK_OF_YEAR), HOUR (HOUR_OF_DAY) etc.

def(MonthName.class, MonthName::new, "MONTHNAME"),
def(Quarter.class, Quarter::new),
// Math
def(Abs.class, Abs::new),
def(ACos.class, ACos::new),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,22 @@
import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.BinaryArithmeticProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.UnaryArithmeticProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NamedDateTimeProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.QuarterProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.BucketExtractorProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.ChainingProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.ConstantProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.HitExtractorProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.StringProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.BinaryStringNumericProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.BinaryStringStringProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.ConcatFunctionProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.InsertFunctionProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.LocateFunctionProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.ReplaceFunctionProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.StringProcessor;
import org.elasticsearch.xpack.sql.expression.function.scalar.string.SubstringFunctionProcessor;

import java.util.ArrayList;
Expand Down Expand Up @@ -52,6 +54,8 @@ public static List<NamedWriteableRegistry.Entry> getNamedWriteables() {
entries.add(new Entry(Processor.class, BinaryMathProcessor.NAME, BinaryMathProcessor::new));
// datetime
entries.add(new Entry(Processor.class, DateTimeProcessor.NAME, DateTimeProcessor::new));
entries.add(new Entry(Processor.class, NamedDateTimeProcessor.NAME, NamedDateTimeProcessor::new));
entries.add(new Entry(Processor.class, QuarterProcessor.NAME, QuarterProcessor::new));
// math
entries.add(new Entry(Processor.class, MathProcessor.NAME, MathProcessor::new));
// string
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;

import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.Expressions;
import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunctionAttribute;
import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;

import java.util.TimeZone;

abstract class BaseDateTimeFunction extends UnaryScalarFunction {

private final TimeZone timeZone;
private final String name;

BaseDateTimeFunction(Location location, Expression field, TimeZone timeZone) {
super(location, field);
this.timeZone = timeZone;

StringBuilder sb = new StringBuilder(super.name());
// add timezone as last argument
sb.insert(sb.length() - 1, " [" + timeZone.getID() + "]");

this.name = sb.toString();
}

@Override
protected final NodeInfo<BaseDateTimeFunction> info() {
return NodeInfo.create(this, ctorForInfo(), field(), timeZone());
}

protected abstract NodeInfo.NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo();

@Override
protected TypeResolution resolveType() {
if (field().dataType() == DataType.DATE) {
return TypeResolution.TYPE_RESOLVED;
}
return new TypeResolution("Function [" + functionName() + "] cannot be applied on a non-date expression (["
+ Expressions.name(field()) + "] of type [" + field().dataType().esType + "])");
}

public TimeZone timeZone() {
return timeZone;
}

@Override
public String name() {
return name;
}

@Override
public boolean foldable() {
return field().foldable();
}

@Override
protected ScriptTemplate asScriptFrom(AggregateFunctionAttribute aggregate) {
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;

import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor;
import org.joda.time.ReadableInstant;

import java.io.IOException;
import java.util.TimeZone;

public abstract class BaseDateTimeProcessor implements Processor {

private final TimeZone timeZone;

BaseDateTimeProcessor(TimeZone timeZone) {
this.timeZone = timeZone;
}

BaseDateTimeProcessor(StreamInput in) throws IOException {
timeZone = TimeZone.getTimeZone(in.readString());
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(timeZone.getID());
}

TimeZone timeZone() {
return timeZone;
}

@Override
public Object process(Object l) {
if (l == null) {
return null;
}
long millis;
if (l instanceof String) {
// 6.4+
millis = Long.parseLong(l.toString());
} else if (l instanceof ReadableInstant) {
// 6.3-
millis = ((ReadableInstant) l).getMillis();
} else {
throw new SqlIllegalArgumentException("A string or a date is required; received {}", l);
}

return doProcess(millis);
}

abstract Object doProcess(long millis);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,14 @@
package org.elasticsearch.xpack.sql.expression.function.scalar.datetime;

import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.Expressions;
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunctionAttribute;
import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor;
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinition;
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinitions;
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.UnaryProcessorDefinition;
import org.elasticsearch.xpack.sql.expression.function.scalar.script.ParamsBuilder;
import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate;
import org.elasticsearch.xpack.sql.tree.Location;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import org.elasticsearch.xpack.sql.type.DataType;
import org.joda.time.DateTime;

Expand All @@ -31,45 +27,10 @@
import static org.elasticsearch.xpack.sql.expression.function.scalar.script.ParamsBuilder.paramsBuilder;
import static org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate.formatTemplate;

public abstract class DateTimeFunction extends UnaryScalarFunction {

private final TimeZone timeZone;
private final String name;
public abstract class DateTimeFunction extends BaseDateTimeFunction {

DateTimeFunction(Location location, Expression field, TimeZone timeZone) {
super(location, field);
this.timeZone = timeZone;

StringBuilder sb = new StringBuilder(super.name());
// add timezone as last argument
sb.insert(sb.length() - 1, " [" + timeZone.getID() + "]");

this.name = sb.toString();
}

@Override
protected final NodeInfo<DateTimeFunction> info() {
return NodeInfo.create(this, ctorForInfo(), field(), timeZone());
}

protected abstract NodeInfo.NodeCtor2<Expression, TimeZone, DateTimeFunction> ctorForInfo();

@Override
protected TypeResolution resolveType() {
if (field().dataType() == DataType.DATE) {
return TypeResolution.TYPE_RESOLVED;
}
return new TypeResolution("Function [" + functionName() + "] cannot be applied on a non-date expression (["
+ Expressions.name(field()) + "] of type [" + field().dataType().esType + "])");
}

public TimeZone timeZone() {
return timeZone;
}

@Override
public boolean foldable() {
return field().foldable();
super(location, field, timeZone);
}

@Override
Expand All @@ -79,7 +40,7 @@ public Object fold() {
return null;
}

return dateTimeChrono(folded.getMillis(), timeZone.getID(), chronoField().name());
return dateTimeChrono(folded.getMillis(), timeZone().getID(), chronoField().name());
}

public static Integer dateTimeChrono(long millis, String tzId, String chronoName) {
Expand All @@ -94,27 +55,21 @@ protected ScriptTemplate asScriptFrom(FieldAttribute field) {
String template = null;
template = formatTemplate("{sql}.dateTimeChrono(doc[{}].value.millis, {}, {})");
params.variable(field.name())
.variable(timeZone.getID())
.variable(timeZone().getID())
.variable(chronoField().name());

return new ScriptTemplate(template, params.build(), dataType());
}


@Override
protected ScriptTemplate asScriptFrom(AggregateFunctionAttribute aggregate) {
throw new UnsupportedOperationException();
}

/**
* Used for generating the painless script version of this function when the time zone is not UTC
*/
protected abstract ChronoField chronoField();

@Override
protected final ProcessorDefinition makeProcessorDefinition() {
protected ProcessorDefinition makeProcessorDefinition() {
return new UnaryProcessorDefinition(location(), this, ProcessorDefinitions.toProcessorDefinition(field()),
new DateTimeProcessor(extractor(), timeZone));
new DateTimeProcessor(extractor(), timeZone()));
}

protected abstract DateTimeExtractor extractor();
Expand All @@ -127,24 +82,18 @@ public DataType dataType() {
// used for applying ranges
public abstract String dateTimeFormat();

// add tz along the rest of the params
@Override
public String name() {
return name;
}

@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
DateTimeFunction other = (DateTimeFunction) obj;
return Objects.equals(other.field(), field())
&& Objects.equals(other.timeZone, timeZone);
&& Objects.equals(other.timeZone(), timeZone());
}

@Override
public int hashCode() {
return Objects.hash(field(), timeZone);
return Objects.hash(field(), timeZone());
}
}
Loading