blob: afd5f5d29c5969461b16136852b5c42bcf18d781 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.drill.exec.expr.fn.impl;
import javax.inject.Inject;
import org.apache.drill.exec.expr.DrillSimpleFunc;
import org.apache.drill.exec.expr.annotations.FunctionTemplate;
import org.apache.drill.exec.expr.annotations.FunctionTemplate.NullHandling;
import org.apache.drill.exec.expr.annotations.Output;
import org.apache.drill.exec.expr.annotations.Param;
import org.apache.drill.exec.expr.annotations.Workspace;
import org.apache.drill.exec.expr.holders.BigIntHolder;
import org.apache.drill.exec.expr.holders.BitHolder;
import org.apache.drill.exec.expr.holders.DateHolder;
import org.apache.drill.exec.expr.holders.IntervalDayHolder;
import org.apache.drill.exec.expr.holders.IntervalHolder;
import org.apache.drill.exec.expr.holders.IntervalYearHolder;
import org.apache.drill.exec.expr.holders.NullableVarCharHolder;
import org.apache.drill.exec.expr.holders.TimeHolder;
import org.apache.drill.exec.expr.holders.TimeStampHolder;
import org.apache.drill.exec.expr.holders.VarCharHolder;
import org.apache.drill.exec.ops.ContextInformation;
import org.apache.drill.exec.physical.impl.project.OutputSizeEstimateConstants;
import io.netty.buffer.DrillBuf;
public class DateTypeFunctions {
/**
* Function to check if a varchar value can be cast to a date.
*
* At the time of writing this function, several other databases were checked
* for behavior compatibility. There was not a consensus between oracle and
* Sql server about the expected behavior of this function, and Postgres
* lacks it completely.
*
* Sql Server appears to have both a DATEFORMAT and language locale setting
* that can change the values accepted by this function. Oracle appears to
* support several formats, some of which are not mentioned in the Sql
* Server docs. With the lack of standardization, we decided to implement
* this function so that it would only consider date strings that would be
* accepted by the cast function as valid.
*/
@SuppressWarnings("unused")
@FunctionTemplate(name = "isdate", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls=NullHandling.INTERNAL,
costCategory = FunctionTemplate.FunctionCostCategory.COMPLEX)
public static class IsDate implements DrillSimpleFunc {
@Param NullableVarCharHolder in;
@Output BitHolder out;
@Override
public void setup() { }
@Override
public void eval() {
// for a null input return false
if (in.isSet == 0) {
out.value = 0;
} else {
out.value = org.apache.drill.exec.expr.fn.impl.StringFunctionHelpers.isReadableAsDate(in.buffer, in.start, in.end) ? 1 : 0;
}
}
}
// Same as above, just for required input
@SuppressWarnings("unused")
@FunctionTemplate(name = "isdate", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls=NullHandling.INTERNAL,
costCategory = FunctionTemplate.FunctionCostCategory.COMPLEX)
public static class IsDateRequiredInput implements DrillSimpleFunc {
@Param VarCharHolder in;
@Output BitHolder out;
@Override
public void setup() { }
@Override
public void eval() {
// for a null input return false
out.value = org.apache.drill.exec.expr.fn.impl.StringFunctionHelpers.isReadableAsDate(in.buffer, in.start, in.end) ? 1 : 0;
}
}
@FunctionTemplate(name = "intervaltype", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class IntervalType implements DrillSimpleFunc {
@Param BigIntHolder inputYears;
@Param BigIntHolder inputMonths;
@Param BigIntHolder inputDays;
@Param BigIntHolder inputHours;
@Param BigIntHolder inputMinutes;
@Param BigIntHolder inputSeconds;
@Param BigIntHolder inputMilliSeconds;
@Output IntervalHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
out.months = (int) ((inputYears.value * org.apache.drill.exec.vector.DateUtilities.yearsToMonths) +
(inputMonths.value));
out.days = (int) inputDays.value;
out.milliseconds = (int) ((inputHours.value * org.apache.drill.exec.vector.DateUtilities.hoursToMillis) +
(inputMinutes.value * org.apache.drill.exec.vector.DateUtilities.minutesToMillis) +
(inputSeconds.value * org.apache.drill.exec.vector.DateUtilities.secondsToMillis) +
(inputMilliSeconds.value));
}
}
@FunctionTemplate(name = "interval_year", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class IntervalYearType implements DrillSimpleFunc {
@Param BigIntHolder inputYears;
@Param BigIntHolder inputMonths;
@Output IntervalYearHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
out.value = (int) ((inputYears.value * org.apache.drill.exec.vector.DateUtilities.yearsToMonths) +
(inputMonths.value));
}
}
@FunctionTemplate(name = "interval_day", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class IntervalDayType implements DrillSimpleFunc {
@Param BigIntHolder inputDays;
@Param BigIntHolder inputHours;
@Param BigIntHolder inputMinutes;
@Param BigIntHolder inputSeconds;
@Param BigIntHolder inputMillis;
@Output IntervalDayHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
out.days = (int) inputDays.value;
out.milliseconds = (int) ((inputHours.value * org.apache.drill.exec.vector.DateUtilities.hoursToMillis) +
(inputMinutes.value * org.apache.drill.exec.vector.DateUtilities.minutesToMillis) +
(inputSeconds.value * org.apache.drill.exec.vector.DateUtilities.secondsToMillis) +
(inputMillis.value));
}
}
@FunctionTemplate(name = "datetype", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class DateType implements DrillSimpleFunc {
@Param BigIntHolder inputYears;
@Param BigIntHolder inputMonths;
@Param BigIntHolder inputDays;
@Output DateHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
out.value = ((new org.joda.time.MutableDateTime((int) inputYears.value,
(int) inputMonths.value,
(int)inputDays.value,
0,
0,
0,
0,
org.joda.time.DateTimeZone.UTC))).getMillis();
}
}
@FunctionTemplate(name = "timestamptype", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class TimeStampType implements DrillSimpleFunc {
@Param BigIntHolder inputYears;
@Param BigIntHolder inputMonths;
@Param BigIntHolder inputDays;
@Param BigIntHolder inputHours;
@Param BigIntHolder inputMinutes;
@Param BigIntHolder inputSeconds;
@Param BigIntHolder inputMilliSeconds;
@Output TimeStampHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
out.value = ((new org.joda.time.MutableDateTime((int)inputYears.value,
(int)inputMonths.value,
(int)inputDays.value,
(int)inputHours.value,
(int)inputMinutes.value,
(int)inputSeconds.value,
(int)inputMilliSeconds.value,
org.joda.time.DateTimeZone.UTC))).getMillis();
}
}
@FunctionTemplate(name = "timetype", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class TimeType implements DrillSimpleFunc {
@Param BigIntHolder inputHours;
@Param BigIntHolder inputMinutes;
@Param BigIntHolder inputSeconds;
@Param BigIntHolder inputMilliSeconds;
@Output TimeHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
out.value = (int) ((inputHours.value * org.apache.drill.exec.vector.DateUtilities.hoursToMillis) +
(inputMinutes.value * org.apache.drill.exec.vector.DateUtilities.minutesToMillis) +
(inputSeconds.value * org.apache.drill.exec.vector.DateUtilities.secondsToMillis) +
inputMilliSeconds.value);
}
}
@FunctionTemplate(name = "current_date", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL, isNiladic = true)
public static class CurrentDate implements DrillSimpleFunc {
@Workspace long queryStartDate;
@Output DateHolder out;
@Inject ContextInformation contextInfo;
@Override
public void setup() {
int timeZoneIndex = contextInfo.getRootFragmentTimeZone();
org.joda.time.DateTimeZone timeZone = org.joda.time.DateTimeZone.forID(org.apache.drill.exec.expr.fn.impl.DateUtility.getTimeZone(timeZoneIndex));
org.joda.time.DateTime now = new org.joda.time.DateTime(contextInfo.getQueryStartTime(), timeZone);
queryStartDate = (new org.joda.time.DateMidnight(now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(), timeZone)).
withZoneRetainFields(org.joda.time.DateTimeZone.UTC).getMillis();
}
@Override
public void eval() {
out.value = queryStartDate;
}
}
@FunctionTemplate(name = "timeofday", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL, isRandom = true,
outputSizeEstimate = OutputSizeEstimateConstants.DATE_TIME_LENGTH)
public static class TimeOfDay implements DrillSimpleFunc {
@Inject DrillBuf buffer;
@Output VarCharHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
java.time.ZonedDateTime temp = java.time.ZonedDateTime.now();
String str = org.apache.drill.exec.expr.fn.impl.DateUtility.formatTimeStampTZ.format(temp);
out.buffer = buffer;
out.start = 0;
out.end = Math.min(100, str.length()); // truncate if target type has length smaller than that of input's string
out.buffer.setBytes(0, str.substring(0,out.end).getBytes());
}
}
/*
* Return query start time in milliseconds
*/
public static long getQueryStartDate(ContextInformation contextInfo) {
org.joda.time.DateTime now = (new org.joda.time.DateTime(contextInfo.getQueryStartTime())).withZoneRetainFields(org.joda.time.DateTimeZone.UTC);
return now.getMillis();
}
/*
* Niladic version of LocalTimeStamp
*/
@FunctionTemplate(names = {"localtimestamp", "current_timestamp"}, scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL, isNiladic = true)
public static class LocalTimeStampNiladic implements DrillSimpleFunc {
@Workspace long queryStartDate;
@Output TimeStampHolder out;
@Inject ContextInformation contextInfo;
@Override
public void setup() {
queryStartDate = org.apache.drill.exec.expr.fn.impl.DateTypeFunctions.getQueryStartDate(contextInfo);
}
@Override
public void eval() {
out.value = queryStartDate;
}
}
/*
* Non-Niladic version of LocalTimeStamp
*/
@FunctionTemplate(names = {"now", "statement_timestamp", "transaction_timestamp"}, scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class LocalTimeStampNonNiladic implements DrillSimpleFunc {
@Workspace long queryStartDate;
@Output TimeStampHolder out;
@Inject ContextInformation contextInfo;
@Override
public void setup() {
queryStartDate = org.apache.drill.exec.expr.fn.impl.DateTypeFunctions.getQueryStartDate(contextInfo);
}
@Override
public void eval() {
out.value = queryStartDate;
}
}
@FunctionTemplate(names = {"current_time", "localtime"}, scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL, isNiladic = true)
public static class CurrentTime implements DrillSimpleFunc {
@Workspace int queryStartTime;
@Output TimeHolder out;
@Inject ContextInformation contextInfo;
@Override
public void setup() {
int timeZoneIndex = contextInfo.getRootFragmentTimeZone();
org.joda.time.DateTimeZone timeZone = org.joda.time.DateTimeZone.forID(org.apache.drill.exec.expr.fn.impl.DateUtility.getTimeZone(timeZoneIndex));
org.joda.time.DateTime now = new org.joda.time.DateTime(contextInfo.getQueryStartTime(), timeZone);
queryStartTime= (now.getHourOfDay() * org.apache.drill.exec.vector.DateUtilities.hoursToMillis) +
(now.getMinuteOfHour() * org.apache.drill.exec.vector.DateUtilities.minutesToMillis) +
(now.getSecondOfMinute() * org.apache.drill.exec.vector.DateUtilities.secondsToMillis) +
(now.getMillisOfSecond());
}
@Override
public void eval() {
out.value = queryStartTime;
}
}
@SuppressWarnings("unused")
@FunctionTemplate(names = {"date_add", "add"}, scope = FunctionTemplate.FunctionScope.SIMPLE, nulls=NullHandling.NULL_IF_NULL)
public static class DateTimeAddFunction implements DrillSimpleFunc {
@Param DateHolder left;
@Param TimeHolder right;
@Output TimeStampHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
out.value = left.value + right.value;
}
}
@SuppressWarnings("unused")
@FunctionTemplate(names = {"date_add", "add"}, scope = FunctionTemplate.FunctionScope.SIMPLE, nulls=NullHandling.NULL_IF_NULL)
public static class TimeDateAddFunction implements DrillSimpleFunc {
@Param TimeHolder right;
@Param DateHolder left;
@Output TimeStampHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
out.value = left.value + right.value;
}
}
/* Dummy function template to allow Optiq to validate this function call.
* At DrillOptiq time we rewrite all date_part() functions to extract functions,
* since they are essentially the same
*/
@SuppressWarnings("unused")
@FunctionTemplate(names = "date_part", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls=NullHandling.NULL_IF_NULL)
public static class DatePartFunction implements DrillSimpleFunc {
@Param VarCharHolder left;
@Param DateHolder right;
@Output BigIntHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
if (1 == 1) {
throw new UnsupportedOperationException("date_part function should be rewritten as extract() functions");
}
}
}
@FunctionTemplate(name = "age", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class AgeTimeStampFunction implements DrillSimpleFunc {
@Param TimeStampHolder left;
@Param TimeStampHolder right;
@Output IntervalHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
long diff = left.value - right.value;
long days = diff / org.apache.drill.exec.vector.DateUtilities.daysToStandardMillis;
out.months = (int) (days / org.apache.drill.exec.vector.DateUtilities.monthToStandardDays);
out.days = (int) (days % org.apache.drill.exec.vector.DateUtilities.monthToStandardDays);
out.milliseconds = (int) (diff % org.apache.drill.exec.vector.DateUtilities.daysToStandardMillis);
}
}
@FunctionTemplate(name = "age", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class AgeTimeStamp2Function implements DrillSimpleFunc {
@Param TimeStampHolder right;
@Workspace long queryStartDate;
@Output IntervalHolder out;
@Inject ContextInformation contextInfo;
@Override
public void setup() {
int timeZoneIndex = contextInfo.getRootFragmentTimeZone();
org.joda.time.DateTimeZone timeZone = org.joda.time.DateTimeZone.forID(org.apache.drill.exec.expr.fn.impl.DateUtility.getTimeZone(timeZoneIndex));
org.joda.time.DateTime now = new org.joda.time.DateTime(contextInfo.getQueryStartTime(), timeZone);
queryStartDate = (new org.joda.time.DateMidnight(now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(), timeZone)).getMillis();
}
@Override
public void eval() {
long diff = queryStartDate - right.value;
long days = diff / org.apache.drill.exec.vector.DateUtilities.daysToStandardMillis;
out.months = (int) (days / org.apache.drill.exec.vector.DateUtilities.monthToStandardDays);
out.days = (int) (days % org.apache.drill.exec.vector.DateUtilities.monthToStandardDays);
out.milliseconds = (int) (diff % org.apache.drill.exec.vector.DateUtilities.daysToStandardMillis);
}
}
@FunctionTemplate(name = "age", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class AgeDateFunction implements DrillSimpleFunc {
@Param DateHolder left;
@Param DateHolder right;
@Output IntervalHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
long diff = left.value - right.value;
long days = diff / org.apache.drill.exec.vector.DateUtilities.daysToStandardMillis;
out.months = (int) (days / org.apache.drill.exec.vector.DateUtilities.monthToStandardDays);
out.days = (int) (days % org.apache.drill.exec.vector.DateUtilities.monthToStandardDays);
out.milliseconds = (int) (diff % org.apache.drill.exec.vector.DateUtilities.daysToStandardMillis);
}
}
@FunctionTemplate(name = "age", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class AgeDate2Function implements DrillSimpleFunc {
@Param DateHolder right;
@Workspace long queryStartDate;
@Output IntervalHolder out;
@Inject ContextInformation contextInfo;
@Override
public void setup() {
int timeZoneIndex = contextInfo.getRootFragmentTimeZone();
org.joda.time.DateTimeZone timeZone = org.joda.time.DateTimeZone.forID(org.apache.drill.exec.expr.fn.impl.DateUtility.getTimeZone(timeZoneIndex));
org.joda.time.DateTime now = new org.joda.time.DateTime(contextInfo.getQueryStartTime(), timeZone);
queryStartDate = (new org.joda.time.DateMidnight(now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(), timeZone)).getMillis();
}
@Override
public void eval() {
long diff = queryStartDate - right.value;
long days = diff / org.apache.drill.exec.vector.DateUtilities.daysToStandardMillis;
out.months = (int) (days / org.apache.drill.exec.vector.DateUtilities.monthToStandardDays);
out.days = (int) (days % org.apache.drill.exec.vector.DateUtilities.monthToStandardDays);
out.milliseconds = (int) (diff % org.apache.drill.exec.vector.DateUtilities.daysToStandardMillis);
}
}
@FunctionTemplate(name = "castTIME", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class CastTimeStampToTime implements DrillSimpleFunc {
@Param TimeStampHolder in;
@Output TimeHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
out.value = (int) (in.value % org.apache.drill.exec.vector.DateUtilities.daysToStandardMillis);
}
}
@FunctionTemplate(name = "castTIME", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class CastDateToTime implements DrillSimpleFunc {
@Param DateHolder in;
@Output TimeHolder out;
@Override
public void setup() {
}
@Override
public void eval() {
out.value = 0;
}
}
@FunctionTemplate(name = "unix_timestamp", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class UnixTimeStamp implements DrillSimpleFunc {
@Output BigIntHolder out;
@Workspace long queryStartDate;
@Inject ContextInformation contextInfo;
@Override
public void setup() {
queryStartDate = contextInfo.getQueryStartTime();
}
@Override
public void eval() {
out.value = queryStartDate / 1000;
}
}
@FunctionTemplate(name = "unix_timestamp", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class UnixTimeStampForDate implements DrillSimpleFunc {
@Param VarCharHolder inputDateValue;
@Output BigIntHolder out;
@Workspace org.joda.time.DateTime date;
@Workspace org.joda.time.format.DateTimeFormatter formatter;
@Override
public void setup() {
formatter = org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
}
@Override
public void eval() {
String inputDate = org.apache.drill.exec.expr.fn.impl.StringFunctionHelpers.toStringFromUTF8(inputDateValue.start, inputDateValue.end, inputDateValue.buffer);
date = formatter.parseDateTime(inputDate);
out.value = date.getMillis() / 1000;
}
}
@FunctionTemplate(name = "unix_timestamp", scope = FunctionTemplate.FunctionScope.SIMPLE, nulls = NullHandling.NULL_IF_NULL)
public static class UnixTimeStampForDateWithPattern implements DrillSimpleFunc {
@Param VarCharHolder inputDateValue;
@Param VarCharHolder inputPattern;
@Output BigIntHolder out;
@Workspace org.joda.time.DateTime date;
@Workspace org.joda.time.format.DateTimeFormatter formatter;
@Override
public void setup() {
String pattern = org.apache.drill.exec.expr.fn.impl.StringFunctionHelpers.toStringFromUTF8(inputPattern.start, inputPattern.end, inputPattern.buffer);
formatter = org.joda.time.format.DateTimeFormat.forPattern(pattern);
}
@Override
public void eval() {
String inputDate = org.apache.drill.exec.expr.fn.impl.StringFunctionHelpers.toStringFromUTF8(inputDateValue.start, inputDateValue.end, inputDateValue.buffer);
date = formatter.parseDateTime(inputDate);
out.value = date.getMillis() / 1000;
}
}
}