SQL: Adds MONTHNAME, DAYNAME and QUARTER functions (#33411)
* Added monthname, dayname and quarter functions * Updated docs tests with the new functions
This commit is contained in:
parent
f598297f55
commit
a3e1f1e46f
|
@ -21,13 +21,16 @@ import org.elasticsearch.xpack.sql.expression.function.aggregate.Sum;
|
|||
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;
|
||||
|
@ -62,21 +65,21 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.string.Ascii;
|
|||
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;
|
||||
|
@ -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"),
|
||||
def(MonthName.class, MonthName::new, "MONTHNAME"),
|
||||
def(Quarter.class, Quarter::new),
|
||||
// Math
|
||||
def(Abs.class, Abs::new),
|
||||
def(ACos.class, ACos::new),
|
||||
|
|
|
@ -10,6 +10,8 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry;
|
|||
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;
|
||||
|
@ -17,13 +19,13 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.
|
|||
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;
|
||||
|
@ -52,6 +54,8 @@ public final class Processors {
|
|||
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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -6,10 +6,7 @@
|
|||
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;
|
||||
|
@ -17,7 +14,6 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definiti
|
|||
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;
|
||||
|
||||
|
@ -31,45 +27,10 @@ import java.util.TimeZone;
|
|||
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
|
||||
|
@ -79,7 +40,7 @@ public abstract class DateTimeFunction extends UnaryScalarFunction {
|
|||
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) {
|
||||
|
@ -94,27 +55,21 @@ public abstract class DateTimeFunction extends UnaryScalarFunction {
|
|||
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();
|
||||
|
@ -127,12 +82,6 @@ public abstract class DateTimeFunction extends UnaryScalarFunction {
|
|||
// 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()) {
|
||||
|
@ -140,11 +89,11 @@ public abstract class DateTimeFunction extends UnaryScalarFunction {
|
|||
}
|
||||
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());
|
||||
}
|
||||
}
|
|
@ -7,19 +7,16 @@ 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.DateTime;
|
||||
import org.joda.time.DateTimeFieldType;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.ReadableDateTime;
|
||||
import org.joda.time.ReadableInstant;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class DateTimeProcessor implements Processor {
|
||||
public class DateTimeProcessor extends BaseDateTimeProcessor {
|
||||
|
||||
public enum DateTimeExtractor {
|
||||
DAY_OF_MONTH(DateTimeFieldType.dayOfMonth()),
|
||||
|
@ -45,24 +42,22 @@ public class DateTimeProcessor implements Processor {
|
|||
}
|
||||
|
||||
public static final String NAME = "dt";
|
||||
|
||||
private final DateTimeExtractor extractor;
|
||||
private final TimeZone timeZone;
|
||||
|
||||
public DateTimeProcessor(DateTimeExtractor extractor, TimeZone timeZone) {
|
||||
super(timeZone);
|
||||
this.extractor = extractor;
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
public DateTimeProcessor(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
extractor = in.readEnum(DateTimeExtractor.class);
|
||||
timeZone = TimeZone.getTimeZone(in.readString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeEnum(extractor);
|
||||
out.writeString(timeZone.getID());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,32 +70,15 @@ public class DateTimeProcessor implements Processor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object process(Object l) {
|
||||
if (l == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ReadableDateTime dt;
|
||||
if (l instanceof String) {
|
||||
// 6.4+
|
||||
final long millis = Long.parseLong(l.toString());
|
||||
dt = new DateTime(millis, DateTimeZone.forTimeZone(timeZone));
|
||||
} else if (l instanceof ReadableInstant) {
|
||||
// 6.3-
|
||||
dt = (ReadableDateTime) l;
|
||||
if (!TimeZone.getTimeZone("UTC").equals(timeZone)) {
|
||||
dt = dt.toDateTime().withZone(DateTimeZone.forTimeZone(timeZone));
|
||||
}
|
||||
} else {
|
||||
throw new SqlIllegalArgumentException("A string or a date is required; received {}", l);
|
||||
}
|
||||
public Object doProcess(long millis) {
|
||||
ReadableDateTime dt = new DateTime(millis, DateTimeZone.forTimeZone(timeZone()));
|
||||
|
||||
return extractor.extract(dt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(extractor, timeZone);
|
||||
return Objects.hash(extractor, timeZone());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -110,7 +88,7 @@ public class DateTimeProcessor implements Processor {
|
|||
}
|
||||
DateTimeProcessor other = (DateTimeProcessor) obj;
|
||||
return Objects.equals(extractor, other.extractor)
|
||||
&& Objects.equals(timeZone, other.timeZone);
|
||||
&& Objects.equals(timeZone(), other.timeZone());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.function.scalar.datetime.NamedDateTimeProcessor.NameExtractor;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Extract the day of the week from a datetime in text format (Monday, Tuesday etc.)
|
||||
*/
|
||||
public class DayName extends NamedDateTimeFunction {
|
||||
protected static final String DAY_NAME_FORMAT = "EEEE";
|
||||
|
||||
public DayName(Location location, Expression field, TimeZone timeZone) {
|
||||
super(location, field, timeZone);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
|
||||
return DayName::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DayName replaceChild(Expression newChild) {
|
||||
return new DayName(location(), newChild, timeZone());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String dateTimeFormat() {
|
||||
return DAY_NAME_FORMAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NameExtractor nameExtractor() {
|
||||
return NameExtractor.DAY_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractName(long millis, String tzId) {
|
||||
return nameExtractor().extract(millis, tzId);
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ public class DayOfMonth extends DateTimeFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected NodeCtor2<Expression, TimeZone, DateTimeFunction> ctorForInfo() {
|
||||
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
|
||||
return DayOfMonth::new;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public class DayOfWeek extends DateTimeFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected NodeCtor2<Expression, TimeZone, DateTimeFunction> ctorForInfo() {
|
||||
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
|
||||
return DayOfWeek::new;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ public class DayOfYear extends DateTimeFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected NodeCtor2<Expression, TimeZone, DateTimeFunction> ctorForInfo() {
|
||||
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
|
||||
return DayOfYear::new;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public class HourOfDay extends DateTimeFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected NodeCtor2<Expression, TimeZone, DateTimeFunction> ctorForInfo() {
|
||||
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
|
||||
return HourOfDay::new;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ public class MinuteOfDay extends DateTimeFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected NodeCtor2<Expression, TimeZone, DateTimeFunction> ctorForInfo() {
|
||||
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
|
||||
return MinuteOfDay::new;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public class MinuteOfHour extends DateTimeFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected NodeCtor2<Expression, TimeZone, DateTimeFunction> ctorForInfo() {
|
||||
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
|
||||
return MinuteOfHour::new;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.function.scalar.datetime.NamedDateTimeProcessor.NameExtractor;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Extract the month from a datetime in text format (January, February etc.)
|
||||
*/
|
||||
public class MonthName extends NamedDateTimeFunction {
|
||||
protected static final String MONTH_NAME_FORMAT = "MMMM";
|
||||
|
||||
public MonthName(Location location, Expression field, TimeZone timeZone) {
|
||||
super(location, field, timeZone);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
|
||||
return MonthName::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MonthName replaceChild(Expression newChild) {
|
||||
return new MonthName(location(), newChild, timeZone());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String dateTimeFormat() {
|
||||
return MONTH_NAME_FORMAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractName(long millis, String tzId) {
|
||||
return nameExtractor().extract(millis, tzId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NameExtractor nameExtractor() {
|
||||
return NameExtractor.MONTH_NAME;
|
||||
}
|
||||
|
||||
}
|
|
@ -22,7 +22,7 @@ public class MonthOfYear extends DateTimeFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected NodeCtor2<Expression, TimeZone, DateTimeFunction> ctorForInfo() {
|
||||
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
|
||||
return MonthOfYear::new;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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.FieldAttribute;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NamedDateTimeProcessor.NameExtractor;
|
||||
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.type.DataType;
|
||||
import org.elasticsearch.xpack.sql.util.StringUtils;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.script.ParamsBuilder.paramsBuilder;
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate.formatTemplate;
|
||||
|
||||
/*
|
||||
* Base class for "naming" date/time functions like month_name and day_name
|
||||
*/
|
||||
abstract class NamedDateTimeFunction extends BaseDateTimeFunction {
|
||||
|
||||
NamedDateTimeFunction(Location location, Expression field, TimeZone timeZone) {
|
||||
super(location, field, timeZone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fold() {
|
||||
DateTime folded = (DateTime) field().fold();
|
||||
if (folded == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return extractName(folded.getMillis(), timeZone().getID());
|
||||
}
|
||||
|
||||
public abstract String extractName(long millis, String tzId);
|
||||
|
||||
@Override
|
||||
protected ScriptTemplate asScriptFrom(FieldAttribute field) {
|
||||
ParamsBuilder params = paramsBuilder();
|
||||
|
||||
String template = null;
|
||||
template = formatTemplate(formatMethodName("{sql}.{method_name}(doc[{}].value.millis, {})"));
|
||||
params.variable(field.name())
|
||||
.variable(timeZone().getID());
|
||||
|
||||
return new ScriptTemplate(template, params.build(), dataType());
|
||||
}
|
||||
|
||||
private String formatMethodName(String template) {
|
||||
// the Painless method name will be the enum's lower camelcase name
|
||||
return template.replace("{method_name}", StringUtils.underscoreToLowerCamelCase(nameExtractor().toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ProcessorDefinition makeProcessorDefinition() {
|
||||
return new UnaryProcessorDefinition(location(), this, ProcessorDefinitions.toProcessorDefinition(field()),
|
||||
new NamedDateTimeProcessor(nameExtractor(), timeZone()));
|
||||
}
|
||||
|
||||
protected abstract NameExtractor nameExtractor();
|
||||
|
||||
protected abstract String dateTimeFormat();
|
||||
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.KEYWORD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
NamedDateTimeFunction other = (NamedDateTimeFunction) obj;
|
||||
return Objects.equals(other.field(), field())
|
||||
&& Objects.equals(other.timeZone(), timeZone());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(field(), timeZone());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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 java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class NamedDateTimeProcessor extends BaseDateTimeProcessor {
|
||||
|
||||
public enum NameExtractor {
|
||||
// for the moment we'll use no specific Locale, but we might consider introducing a Locale parameter, just like the timeZone one
|
||||
DAY_NAME((Long millis, String tzId) -> {
|
||||
ZonedDateTime time = ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId));
|
||||
return time.format(DateTimeFormatter.ofPattern(DayName.DAY_NAME_FORMAT, Locale.ROOT));
|
||||
}),
|
||||
MONTH_NAME((Long millis, String tzId) -> {
|
||||
ZonedDateTime time = ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId));
|
||||
return time.format(DateTimeFormatter.ofPattern(MonthName.MONTH_NAME_FORMAT, Locale.ROOT));
|
||||
});
|
||||
|
||||
private final BiFunction<Long,String,String> apply;
|
||||
|
||||
NameExtractor(BiFunction<Long,String,String> apply) {
|
||||
this.apply = apply;
|
||||
}
|
||||
|
||||
public final String extract(Long millis, String tzId) {
|
||||
return apply.apply(millis, tzId);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String NAME = "ndt";
|
||||
|
||||
private final NameExtractor extractor;
|
||||
|
||||
public NamedDateTimeProcessor(NameExtractor extractor, TimeZone timeZone) {
|
||||
super(timeZone);
|
||||
this.extractor = extractor;
|
||||
}
|
||||
|
||||
public NamedDateTimeProcessor(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
extractor = in.readEnum(NameExtractor.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeEnum(extractor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
NameExtractor extractor() {
|
||||
return extractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object doProcess(long millis) {
|
||||
return extractor.extract(millis, timeZone().getID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(extractor, timeZone());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
NamedDateTimeProcessor other = (NamedDateTimeProcessor) obj;
|
||||
return Objects.equals(extractor, other.extractor)
|
||||
&& Objects.equals(timeZone(), other.timeZone());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return extractor.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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.FieldAttribute;
|
||||
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.NodeCtor2;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.QuarterProcessor.quarter;
|
||||
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 class Quarter extends BaseDateTimeFunction {
|
||||
|
||||
protected static final String QUARTER_FORMAT = "q";
|
||||
|
||||
public Quarter(Location location, Expression field, TimeZone timeZone) {
|
||||
super(location, field, timeZone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fold() {
|
||||
DateTime folded = (DateTime) field().fold();
|
||||
if (folded == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return quarter(folded.getMillis(), timeZone().getID());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ScriptTemplate asScriptFrom(FieldAttribute field) {
|
||||
ParamsBuilder params = paramsBuilder();
|
||||
|
||||
String template = null;
|
||||
template = formatTemplate("{sql}.quarter(doc[{}].value.millis, {})");
|
||||
params.variable(field.name())
|
||||
.variable(timeZone().getID());
|
||||
|
||||
return new ScriptTemplate(template, params.build(), dataType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
|
||||
return Quarter::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Quarter replaceChild(Expression newChild) {
|
||||
return new Quarter(location(), newChild, timeZone());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProcessorDefinition makeProcessorDefinition() {
|
||||
return new UnaryProcessorDefinition(location(), this, ProcessorDefinitions.toProcessorDefinition(field()),
|
||||
new QuarterProcessor(timeZone()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.INTEGER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
BaseDateTimeFunction other = (BaseDateTimeFunction) obj;
|
||||
return Objects.equals(other.field(), field())
|
||||
&& Objects.equals(other.timeZone(), timeZone());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(field(), timeZone());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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 java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class QuarterProcessor extends BaseDateTimeProcessor {
|
||||
|
||||
public QuarterProcessor(TimeZone timeZone) {
|
||||
super(timeZone);
|
||||
}
|
||||
|
||||
public QuarterProcessor(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public static final String NAME = "q";
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object doProcess(long millis) {
|
||||
return quarter(millis, timeZone().getID());
|
||||
}
|
||||
|
||||
public static Integer quarter(long millis, String tzId) {
|
||||
ZonedDateTime time = ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId));
|
||||
return Integer.valueOf(time.format(DateTimeFormatter.ofPattern(Quarter.QUARTER_FORMAT, Locale.ROOT)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(timeZone());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
DateTimeProcessor other = (DateTimeProcessor) obj;
|
||||
return Objects.equals(timeZone(), other.timeZone());
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ public class SecondOfMinute extends DateTimeFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected NodeCtor2<Expression, TimeZone, DateTimeFunction> ctorForInfo() {
|
||||
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
|
||||
return SecondOfMinute::new;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public class WeekOfYear extends DateTimeFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected NodeCtor2<Expression, TimeZone, DateTimeFunction> ctorForInfo() {
|
||||
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
|
||||
return WeekOfYear::new;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public class Year extends DateTimeHistogramFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected NodeCtor2<Expression, TimeZone, DateTimeFunction> ctorForInfo() {
|
||||
protected NodeCtor2<Expression, TimeZone, BaseDateTimeFunction> ctorForInfo() {
|
||||
return Year::new;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function.scalar.whitelist;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NamedDateTimeProcessor.NameExtractor;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.QuarterProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.BinaryStringNumericProcessor.BinaryStringNumericOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.BinaryStringStringProcessor.BinaryStringStringOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.ConcatFunctionProcessor;
|
||||
|
@ -28,6 +30,18 @@ public final class InternalSqlScriptUtils {
|
|||
return DateTimeFunction.dateTimeChrono(millis, tzId, chronoName);
|
||||
}
|
||||
|
||||
public static String dayName(long millis, String tzId) {
|
||||
return NameExtractor.DAY_NAME.extract(millis, tzId);
|
||||
}
|
||||
|
||||
public static String monthName(long millis, String tzId) {
|
||||
return NameExtractor.MONTH_NAME.extract(millis, tzId);
|
||||
}
|
||||
|
||||
public static Integer quarter(long millis, String tzId) {
|
||||
return QuarterProcessor.quarter(millis, tzId);
|
||||
}
|
||||
|
||||
public static Integer ascii(String s) {
|
||||
return (Integer) StringOperation.ASCII.apply(s);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalSqlScriptUtils {
|
||||
|
||||
Integer dateTimeChrono(long, String, String)
|
||||
String dayName(long, String)
|
||||
String monthName(long, String)
|
||||
Integer quarter(long, String)
|
||||
Integer ascii(String)
|
||||
Integer bitLength(String)
|
||||
String character(Number)
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.Writeable.Reader;
|
||||
import org.elasticsearch.test.AbstractWireSerializingTestCase;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.NamedDateTimeProcessor.NameExtractor;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class NamedDateTimeProcessorTests extends AbstractWireSerializingTestCase<NamedDateTimeProcessor> {
|
||||
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
||||
|
||||
public static NamedDateTimeProcessor randomNamedDateTimeProcessor() {
|
||||
return new NamedDateTimeProcessor(randomFrom(NameExtractor.values()), UTC);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedDateTimeProcessor createTestInstance() {
|
||||
return randomNamedDateTimeProcessor();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Reader<NamedDateTimeProcessor> instanceReader() {
|
||||
return NamedDateTimeProcessor::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedDateTimeProcessor mutateInstance(NamedDateTimeProcessor instance) throws IOException {
|
||||
NameExtractor replaced = randomValueOtherThan(instance.extractor(), () -> randomFrom(NameExtractor.values()));
|
||||
return new NamedDateTimeProcessor(replaced, UTC);
|
||||
}
|
||||
|
||||
public void testValidDayNamesInUTC() {
|
||||
NamedDateTimeProcessor proc = new NamedDateTimeProcessor(NameExtractor.DAY_NAME, UTC);
|
||||
assertEquals("Thursday", proc.process("0"));
|
||||
assertEquals("Saturday", proc.process("-64164233612338"));
|
||||
assertEquals("Monday", proc.process("64164233612338"));
|
||||
|
||||
assertEquals("Thursday", proc.process(new DateTime(0L, DateTimeZone.UTC)));
|
||||
assertEquals("Thursday", proc.process(new DateTime(-5400, 12, 25, 2, 0, DateTimeZone.UTC)));
|
||||
assertEquals("Friday", proc.process(new DateTime(30, 2, 1, 12, 13, DateTimeZone.UTC)));
|
||||
assertEquals("Tuesday", proc.process(new DateTime(10902, 8, 22, 11, 11, DateTimeZone.UTC)));
|
||||
}
|
||||
|
||||
public void testValidDayNamesWithNonUTCTimeZone() {
|
||||
NamedDateTimeProcessor proc = new NamedDateTimeProcessor(NameExtractor.DAY_NAME, TimeZone.getTimeZone("GMT-10:00"));
|
||||
assertEquals("Wednesday", proc.process("0"));
|
||||
assertEquals("Friday", proc.process("-64164233612338"));
|
||||
assertEquals("Monday", proc.process("64164233612338"));
|
||||
|
||||
assertEquals("Wednesday", proc.process(new DateTime(0L, DateTimeZone.UTC)));
|
||||
assertEquals("Wednesday", proc.process(new DateTime(-5400, 12, 25, 2, 0, DateTimeZone.UTC)));
|
||||
assertEquals("Friday", proc.process(new DateTime(30, 2, 1, 12, 13, DateTimeZone.UTC)));
|
||||
assertEquals("Tuesday", proc.process(new DateTime(10902, 8, 22, 11, 11, DateTimeZone.UTC)));
|
||||
assertEquals("Monday", proc.process(new DateTime(10902, 8, 22, 9, 59, DateTimeZone.UTC)));
|
||||
}
|
||||
|
||||
public void testValidMonthNamesInUTC() {
|
||||
NamedDateTimeProcessor proc = new NamedDateTimeProcessor(NameExtractor.MONTH_NAME, UTC);
|
||||
assertEquals("January", proc.process("0"));
|
||||
assertEquals("September", proc.process("-64164233612338"));
|
||||
assertEquals("April", proc.process("64164233612338"));
|
||||
|
||||
assertEquals("January", proc.process(new DateTime(0L, DateTimeZone.UTC)));
|
||||
assertEquals("December", proc.process(new DateTime(-5400, 12, 25, 10, 10, DateTimeZone.UTC)));
|
||||
assertEquals("February", proc.process(new DateTime(30, 2, 1, 12, 13, DateTimeZone.UTC)));
|
||||
assertEquals("August", proc.process(new DateTime(10902, 8, 22, 11, 11, DateTimeZone.UTC)));
|
||||
}
|
||||
|
||||
public void testValidMonthNamesWithNonUTCTimeZone() {
|
||||
NamedDateTimeProcessor proc = new NamedDateTimeProcessor(NameExtractor.MONTH_NAME, TimeZone.getTimeZone("GMT-3:00"));
|
||||
assertEquals("December", proc.process("0"));
|
||||
assertEquals("August", proc.process("-64165813612338")); // GMT: Tuesday, September 1, -0064 2:53:07.662 AM
|
||||
assertEquals("April", proc.process("64164233612338")); // GMT: Monday, April 14, 4003 2:13:32.338 PM
|
||||
|
||||
assertEquals("December", proc.process(new DateTime(0L, DateTimeZone.UTC)));
|
||||
assertEquals("November", proc.process(new DateTime(-5400, 12, 1, 1, 1, DateTimeZone.UTC)));
|
||||
assertEquals("February", proc.process(new DateTime(30, 2, 1, 12, 13, DateTimeZone.UTC)));
|
||||
assertEquals("July", proc.process(new DateTime(10902, 8, 1, 2, 59, DateTimeZone.UTC)));
|
||||
assertEquals("August", proc.process(new DateTime(10902, 8, 1, 3, 00, DateTimeZone.UTC)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.test.ESTestCase;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class QuarterProcessorTests extends ESTestCase {
|
||||
|
||||
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
||||
|
||||
public void testQuarterWithUTCTimezone() {
|
||||
QuarterProcessor proc = new QuarterProcessor(UTC);
|
||||
|
||||
assertEquals(1, proc.process(new DateTime(0L, DateTimeZone.UTC)));
|
||||
assertEquals(4, proc.process(new DateTime(-5400, 12, 25, 10, 10, DateTimeZone.UTC)));
|
||||
assertEquals(1, proc.process(new DateTime(30, 2, 1, 12, 13, DateTimeZone.UTC)));
|
||||
assertEquals(3, proc.process(new DateTime(10902, 8, 22, 11, 11, DateTimeZone.UTC)));
|
||||
|
||||
assertEquals(1, proc.process("0"));
|
||||
assertEquals(3, proc.process("-64164233612338"));
|
||||
assertEquals(2, proc.process("64164233612338"));
|
||||
}
|
||||
|
||||
public void testValidDayNamesWithNonUTCTimeZone() {
|
||||
QuarterProcessor proc = new QuarterProcessor(TimeZone.getTimeZone("GMT-10:00"));
|
||||
assertEquals(4, proc.process(new DateTime(0L, DateTimeZone.UTC)));
|
||||
assertEquals(4, proc.process(new DateTime(-5400, 1, 1, 5, 0, DateTimeZone.UTC)));
|
||||
assertEquals(1, proc.process(new DateTime(30, 4, 1, 9, 59, DateTimeZone.UTC)));
|
||||
|
||||
proc = new QuarterProcessor(TimeZone.getTimeZone("GMT+10:00"));
|
||||
assertEquals(4, proc.process(new DateTime(10902, 9, 30, 14, 1, DateTimeZone.UTC)));
|
||||
assertEquals(3, proc.process(new DateTime(10902, 9, 30, 13, 59, DateTimeZone.UTC)));
|
||||
|
||||
assertEquals(1, proc.process("0"));
|
||||
assertEquals(3, proc.process("-64164233612338"));
|
||||
assertEquals(2, proc.process("64164233612338"));
|
||||
}
|
||||
}
|
|
@ -65,6 +65,8 @@ public abstract class ShowTestCase extends CliIntegrationTestCase {
|
|||
assertThat(readLine(), RegexMatcher.matches("\\s*DAY_OF_YEAR\\s*\\|\\s*SCALAR\\s*"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*HOUR_OF_DAY\\s*\\|\\s*SCALAR\\s*"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*MINUTE_OF_DAY\\s*\\|\\s*SCALAR\\s*"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*DAY_NAME\\s*\\|\\s*SCALAR\\s*"));
|
||||
assertThat(readLine(), RegexMatcher.matches("\\s*DAYNAME\\s*\\|\\s*SCALAR\\s*"));
|
||||
assertEquals("", readLine());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,11 @@ MONTH |SCALAR
|
|||
YEAR |SCALAR
|
||||
WEEK_OF_YEAR |SCALAR
|
||||
WEEK |SCALAR
|
||||
DAY_NAME |SCALAR
|
||||
DAYNAME |SCALAR
|
||||
MONTH_NAME |SCALAR
|
||||
MONTHNAME |SCALAR
|
||||
QUARTER |SCALAR
|
||||
ABS |SCALAR
|
||||
ACOS |SCALAR
|
||||
ASIN |SCALAR
|
||||
|
@ -130,6 +135,8 @@ DAY_OF_WEEK |SCALAR
|
|||
DAY_OF_YEAR |SCALAR
|
||||
HOUR_OF_DAY |SCALAR
|
||||
MINUTE_OF_DAY |SCALAR
|
||||
DAY_NAME |SCALAR
|
||||
DAYNAME |SCALAR
|
||||
;
|
||||
|
||||
showTables
|
||||
|
|
|
@ -12,34 +12,83 @@
|
|||
|
||||
dateTimeDay
|
||||
SELECT DAY(birth_date) d, last_name l FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
|
||||
dateTimeDayOfMonth
|
||||
SELECT DAY_OF_MONTH(birth_date) d, last_name l FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
|
||||
dateTimeMonth
|
||||
SELECT MONTH(birth_date) d, last_name l FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
|
||||
dateTimeYear
|
||||
SELECT YEAR(birth_date) d, last_name l FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
|
||||
monthNameFromStringDate
|
||||
SELECT MONTHNAME(CAST('2018-09-03' AS TIMESTAMP)) month FROM "test_emp" limit 1;
|
||||
|
||||
dayNameFromStringDate
|
||||
SELECT DAYNAME(CAST('2018-09-03' AS TIMESTAMP)) day FROM "test_emp" limit 1;
|
||||
|
||||
quarterSelect
|
||||
SELECT QUARTER(hire_date) q, hire_date FROM test_emp ORDER BY hire_date LIMIT 15;
|
||||
|
||||
//
|
||||
// Filter
|
||||
//
|
||||
|
||||
dateTimeFilterDayOfMonth
|
||||
SELECT DAY_OF_MONTH(birth_date) AS d, last_name l FROM "test_emp" WHERE DAY_OF_MONTH(birth_date) <= 10 ORDER BY emp_no LIMIT 5;
|
||||
|
||||
dateTimeFilterMonth
|
||||
SELECT MONTH(birth_date) AS d, last_name l FROM "test_emp" WHERE MONTH(birth_date) <= 5 ORDER BY emp_no LIMIT 5;
|
||||
|
||||
dateTimeFilterYear
|
||||
SELECT YEAR(birth_date) AS d, last_name l FROM "test_emp" WHERE YEAR(birth_date) <= 1960 ORDER BY emp_no LIMIT 5;
|
||||
|
||||
monthNameFilterWithFirstLetter
|
||||
SELECT MONTHNAME(hire_date) AS m, hire_date FROM "test_emp" WHERE LEFT(MONTHNAME(hire_date), 1) = 'J' ORDER BY hire_date LIMIT 10;
|
||||
|
||||
monthNameFilterWithFullName
|
||||
SELECT MONTHNAME(hire_date) AS m, hire_date FROM "test_emp" WHERE MONTHNAME(hire_date) = 'August' ORDER BY hire_date LIMIT 10;
|
||||
|
||||
dayNameFilterWithFullName
|
||||
SELECT DAYNAME(hire_date) AS d, hire_date FROM "test_emp" WHERE DAYNAME(hire_date) = 'Sunday' ORDER BY hire_date LIMIT 10;
|
||||
|
||||
dayNameAndMonthNameAsFilter
|
||||
SELECT first_name, last_name FROM "test_emp" WHERE DAYNAME(hire_date) = 'Sunday' AND MONTHNAME(hire_date) = 'January' ORDER BY hire_date LIMIT 10;
|
||||
|
||||
quarterWithFilter
|
||||
SELECT QUARTER(hire_date) quarter, hire_date FROM test_emp WHERE QUARTER(hire_date) > 2 ORDER BY hire_date LIMIT 15;
|
||||
|
||||
//
|
||||
// Aggregate
|
||||
//
|
||||
|
||||
|
||||
dateTimeAggByYear
|
||||
SELECT YEAR(birth_date) AS d, CAST(SUM(emp_no) AS INT) s FROM "test_emp" GROUP BY YEAR(birth_date) ORDER BY YEAR(birth_date) LIMIT 13;
|
||||
|
||||
dateTimeAggByMonth
|
||||
dateTimeAggByMonthWithOrderBy
|
||||
SELECT MONTH(birth_date) AS d, COUNT(*) AS c, CAST(SUM(emp_no) AS INT) s FROM "test_emp" GROUP BY MONTH(birth_date) ORDER BY MONTH(birth_date) DESC;
|
||||
|
||||
dateTimeAggByDayOfMonth
|
||||
dateTimeAggByDayOfMonthWithOrderBy
|
||||
SELECT DAY_OF_MONTH(birth_date) AS d, COUNT(*) AS c, CAST(SUM(emp_no) AS INT) s FROM "test_emp" GROUP BY DAY_OF_MONTH(birth_date) ORDER BY DAY_OF_MONTH(birth_date) DESC;
|
||||
|
||||
monthNameWithGroupBy
|
||||
SELECT MONTHNAME("hire_date") AS month, COUNT(*) AS count FROM "test_emp" GROUP BY MONTHNAME("hire_date"), MONTH("hire_date") ORDER BY MONTH("hire_date");
|
||||
|
||||
monthNameWithDoubleGroupByAndOrderBy
|
||||
SELECT MONTHNAME("hire_date") AS month, COUNT(*) AS count FROM "test_emp" GROUP BY MONTHNAME("hire_date"), MONTH("hire_date") ORDER BY MONTHNAME("hire_date") DESC;
|
||||
|
||||
// AwaitsFix https://github.com/elastic/elasticsearch/issues/33519
|
||||
// monthNameWithGroupByOrderByAndHaving
|
||||
// SELECT CAST(MAX("salary") AS DOUBLE) max_salary, MONTHNAME("hire_date") month_name FROM "test_emp" GROUP BY MONTHNAME("hire_date") HAVING MAX("salary") > 50000 ORDER BY MONTHNAME(hire_date);
|
||||
// dayNameWithHaving
|
||||
// SELECT DAYNAME("hire_date") FROM "test_emp" GROUP BY DAYNAME("hire_date") HAVING MAX("emp_no") > ASCII(DAYNAME("hire_date"));
|
||||
|
||||
dayNameWithDoubleGroupByAndOrderBy
|
||||
SELECT COUNT(*) c, DAYNAME(hire_date) day_name, DAY(hire_date) day FROM test_emp WHERE MONTHNAME(hire_date) = 'August' GROUP BY DAYNAME(hire_date), DAY(hire_date) ORDER BY DAYNAME(hire_date), DAY(hire_date);
|
||||
|
||||
dayNameWithGroupByOrderByAndHaving
|
||||
SELECT CAST(MAX(salary) AS DOUBLE) max_salary, DAYNAME(hire_date) day_name FROM test_emp GROUP BY DAYNAME(hire_date) HAVING MAX(salary) > 50000 ORDER BY DAYNAME("hire_date");
|
||||
|
||||
quarterWithGroupByAndOrderBy
|
||||
SELECT QUARTER(hire_date) quarter, COUNT(*) hires FROM test_emp GROUP BY QUARTER(hire_date) ORDER BY QUARTER(hire_date);
|
|
@ -214,6 +214,11 @@ MONTH |SCALAR
|
|||
YEAR |SCALAR
|
||||
WEEK_OF_YEAR |SCALAR
|
||||
WEEK |SCALAR
|
||||
DAY_NAME |SCALAR
|
||||
DAYNAME |SCALAR
|
||||
MONTH_NAME |SCALAR
|
||||
MONTHNAME |SCALAR
|
||||
QUARTER |SCALAR
|
||||
ABS |SCALAR
|
||||
ACOS |SCALAR
|
||||
ASIN |SCALAR
|
||||
|
@ -318,7 +323,9 @@ DAY |SCALAR
|
|||
DAY_OF_WEEK |SCALAR
|
||||
DAY_OF_YEAR |SCALAR
|
||||
HOUR_OF_DAY |SCALAR
|
||||
MINUTE_OF_DAY |SCALAR
|
||||
MINUTE_OF_DAY |SCALAR
|
||||
DAY_NAME |SCALAR
|
||||
DAYNAME |SCALAR
|
||||
|
||||
// end::showFunctionsWithPattern
|
||||
;
|
||||
|
|
Loading…
Reference in New Issue