Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 105 additions & 5 deletions be/src/vec/functions/function_date_or_datetime_computation.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "runtime/runtime_state.h"
#include "udf/udf.h"
#include "util/binary_cast.hpp"
#include "util/string_parser.hpp"
#include "vec/aggregate_functions/aggregate_function.h"
#include "vec/columns/column.h"
#include "vec/columns/column_const.h"
Expand Down Expand Up @@ -79,6 +80,7 @@ auto date_time_add(const typename PrimitiveTypeTraits<ArgType>::DataType::FieldT
if (!(ts_value.template date_add_interval<unit>(interval))) [[unlikely]] {
throw_out_of_bound_date_int<ValueType, NativeType>(get_time_unit_name(unit), t, delta);
}

// here DateValueType = ResultDateValueType
return binary_cast<ValueType, NativeType>(ts_value);
}
Expand All @@ -96,6 +98,7 @@ auto date_time_add(const typename PrimitiveTypeTraits<ArgType>::DataType::FieldT
TimeUnit::UNIT == TimeUnit::DAY || TimeUnit::UNIT == TimeUnit::HOUR) \
? PrimitiveType::TYPE_INT \
: PrimitiveType::TYPE_BIGINT; \
static constexpr PrimitiveType IntervalPRealType = PType; \
using InputNativeType = typename PrimitiveTypeTraits<PType>::DataType::FieldType; \
using ReturnNativeType = InputNativeType; \
using IntervalDataType = typename PrimitiveTypeTraits<IntervalPType>::DataType; \
Expand Down Expand Up @@ -124,11 +127,34 @@ ADD_TIME_FUNCTION_IMPL(AddWeeksImpl, weeks_add, WEEK);
ADD_TIME_FUNCTION_IMPL(AddMonthsImpl, months_add, MONTH);
ADD_TIME_FUNCTION_IMPL(AddYearsImpl, years_add, YEAR);

template <PrimitiveType PType>
struct AddDaySecondImpl {
static constexpr PrimitiveType ArgPType = PType;
static constexpr PrimitiveType ReturnType = PType;
static constexpr PrimitiveType IntervalPType = PrimitiveType ::TYPE_INT;
static constexpr PrimitiveType IntervalPRealType = TYPE_STRING;
Copy link
Contributor

Choose a reason for hiding this comment

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

why add this? why cant just use IntervalPType?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is actually a compromise solution. We introduced the IntervalPRealType field to handle cases like AddDaySecondImpl where the delta value passed during the execution of the execute method requires type conversion (StringRef → Integer). (So we cannot directly use the ADD_TIME_FUNCTION_IMPL macro to generate the code).

If we hadn't introduced the IntervalPRealType field and instead set IntervalPType = TYPE_STRING, then in FunctionDateOrDateTimeComputation::execute_impl, we would need to handle TYPE_STRING cases for all three scenarios: vector-vector, vector-const, and const-vector. While the vector-const scenario would be simple to handle, but handling other scenarios will be complex, as we would need to modify all execute methods to support the TYPE_STRING type.

Copy link
Contributor

Choose a reason for hiding this comment

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

this problem is easy to resolve——add requires clause to functions in DateTimeOp to specific if it should exist depending on template argument.

using InputNativeType = typename PrimitiveTypeTraits<PType>::DataType ::FieldType;
using ReturnNativeType = InputNativeType;
using IntervalDataType = typename PrimitiveTypeTraits<IntervalPType>::DataType;
using IntervalNativeType = DataTypeInt32::FieldType;

static constexpr auto name = "day_second_add";
static constexpr auto is_nullable = false;
static inline ReturnNativeType execute(const InputNativeType& t, IntervalNativeType delta) {
return date_time_add<TimeUnit ::SECOND, PType, IntervalNativeType>(t, delta);
}
static DataTypes get_variadic_argument_types() {
return {std ::make_shared<typename PrimitiveTypeTraits<PType>::DataType>(),
std ::make_shared<typename PrimitiveTypeTraits<IntervalPRealType>::DataType>()};
}
};

template <PrimitiveType PType>
struct AddQuartersImpl {
static constexpr PrimitiveType ArgPType = PType;
static constexpr PrimitiveType ReturnType = PType;
static constexpr PrimitiveType IntervalPType = PrimitiveType::TYPE_INT;
static constexpr PrimitiveType IntervalPRealType = TYPE_INT;
using InputNativeType = typename PrimitiveTypeTraits<PType>::DataType::FieldType;
using ReturnNativeType = InputNativeType;

Expand Down Expand Up @@ -163,60 +189,70 @@ struct SubtractIntervalImpl {
template <PrimitiveType DateType>
struct SubtractMicrosecondsImpl : SubtractIntervalImpl<AddMicrosecondsImpl<DateType>> {
static constexpr PrimitiveType IntervalPType = AddMicrosecondsImpl<DateType>::IntervalPType;
static constexpr PrimitiveType IntervalPRealType = AddMicrosecondsImpl<DateType>::IntervalPType;
static constexpr auto name = "microseconds_sub";
};

template <PrimitiveType DateType>
struct SubtractMillisecondsImpl : SubtractIntervalImpl<AddMillisecondsImpl<DateType>> {
static constexpr PrimitiveType IntervalPType = AddMillisecondsImpl<DateType>::IntervalPType;
static constexpr PrimitiveType IntervalPRealType = AddMicrosecondsImpl<DateType>::IntervalPType;
static constexpr auto name = "milliseconds_sub";
};

template <PrimitiveType DateType>
struct SubtractSecondsImpl : SubtractIntervalImpl<AddSecondsImpl<DateType>> {
static constexpr PrimitiveType IntervalPType = AddSecondsImpl<DateType>::IntervalPType;
static constexpr PrimitiveType IntervalPRealType = AddMicrosecondsImpl<DateType>::IntervalPType;
static constexpr auto name = "seconds_sub";
};

template <PrimitiveType DateType>
struct SubtractMinutesImpl : SubtractIntervalImpl<AddMinutesImpl<DateType>> {
static constexpr PrimitiveType IntervalPType = AddMinutesImpl<DateType>::IntervalPType;
static constexpr PrimitiveType IntervalPRealType = AddMicrosecondsImpl<DateType>::IntervalPType;
static constexpr auto name = "minutes_sub";
};

template <PrimitiveType DateType>
struct SubtractHoursImpl : SubtractIntervalImpl<AddHoursImpl<DateType>> {
static constexpr PrimitiveType IntervalPType = AddHoursImpl<DateType>::IntervalPType;
static constexpr PrimitiveType IntervalPRealType = AddMicrosecondsImpl<DateType>::IntervalPType;
static constexpr auto name = "hours_sub";
};

template <PrimitiveType DateType>
struct SubtractDaysImpl : SubtractIntervalImpl<AddDaysImpl<DateType>> {
static constexpr PrimitiveType IntervalPType = AddDaysImpl<DateType>::IntervalPType;
static constexpr PrimitiveType IntervalPRealType = AddMicrosecondsImpl<DateType>::IntervalPType;
static constexpr auto name = "days_sub";
};

template <PrimitiveType DateType>
struct SubtractWeeksImpl : SubtractIntervalImpl<AddWeeksImpl<DateType>> {
static constexpr PrimitiveType IntervalPType = AddWeeksImpl<DateType>::IntervalPType;
static constexpr PrimitiveType IntervalPRealType = AddMicrosecondsImpl<DateType>::IntervalPType;
static constexpr auto name = "weeks_sub";
};

template <PrimitiveType DateType>
struct SubtractMonthsImpl : SubtractIntervalImpl<AddMonthsImpl<DateType>> {
static constexpr PrimitiveType IntervalPType = AddMonthsImpl<DateType>::IntervalPType;
static constexpr PrimitiveType IntervalPRealType = AddMicrosecondsImpl<DateType>::IntervalPType;
static constexpr auto name = "months_sub";
};

template <PrimitiveType DateType>
struct SubtractQuartersImpl : SubtractIntervalImpl<AddQuartersImpl<DateType>> {
static constexpr PrimitiveType IntervalPType = AddQuartersImpl<DateType>::IntervalPType;
static constexpr PrimitiveType IntervalPRealType = AddMicrosecondsImpl<DateType>::IntervalPType;
static constexpr auto name = "quarters_sub";
};

template <PrimitiveType DateType>
struct SubtractYearsImpl : SubtractIntervalImpl<AddYearsImpl<DateType>> {
static constexpr PrimitiveType IntervalPType = AddYearsImpl<DateType>::IntervalPType;
static constexpr PrimitiveType IntervalPRealType = AddMicrosecondsImpl<DateType>::IntervalPType;
static constexpr auto name = "years_sub";
};

Expand Down Expand Up @@ -299,6 +335,7 @@ TIME_DIFF_FUNCTION_IMPL(MicroSecondsDiffImpl, microseconds_diff, MICROSECOND);
struct CLASS { \
static constexpr PrimitiveType ArgPType = DateType; \
static constexpr PrimitiveType IntervalPType = PrimitiveType::TYPE_INT; \
static constexpr PrimitiveType IntervalPRealType = PrimitiveType::TYPE_INT; \
using ArgType = typename PrimitiveTypeTraits<DateType>::DataType::FieldType; \
using IntervalNativeType = \
typename PrimitiveTypeTraits<IntervalPType>::DataType::FieldType; \
Expand Down Expand Up @@ -580,10 +617,72 @@ class FunctionDateOrDateTimeComputation : public IFunction {
// vector-const
if (const auto* nest_col1_const = check_and_get_column<ColumnConst>(*nest_col1)) {
rconst = true;
const auto col1_inside_const =
assert_cast<const IntervalColumnType&>(nest_col1_const->get_data_column());
Op::vector_constant(sources->get_data(), res_col->get_data(),
col1_inside_const.get_data()[0], nullmap0, nullmap1);
if constexpr (Transform::IntervalPRealType == TYPE_STRING) {
Copy link
Contributor

Choose a reason for hiding this comment

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

type is TYPE_STRING could represent day_second just now. shouldn't put the logic here.

Copy link
Contributor

Choose a reason for hiding this comment

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

the specific calculation logic shouldn't put here. should in Op.

StringRef time_str = nest_col1_const->get_data_at(0).trim();
// string format: "d h:m:s"
StringParser::ParseResult success;

size_t space_pos = time_str.find_first_of(' ');
// day
std::string days_sub = time_str.substring(0, space_pos).to_string();
int days = StringParser::string_to_int_internal<int32_t, true>(
days_sub.data(), days_sub.size(), &success);
if (success != StringParser::PARSE_SUCCESS) {
return Status::InvalidArgument("Invalid days format in '{}'",
time_str.to_string());
}

// hour:minute:second
StringRef time_hour_str = time_str.substring(space_pos + 1);
size_t colon1 = time_hour_str.find_first_of(':');
if (colon1 == std::string::npos) {
return Status::InvalidArgument("Invalid time format, missing ':' in '{}'",
time_str.to_string());
}

size_t colon2_rel = time_hour_str.substring(colon1 + 1).find_first_of(':');
size_t colon2 = (colon2_rel != std::string::npos) ? colon1 + 1 + colon2_rel
: std::string::npos;
if (colon2 == std::string::npos) {
return Status::InvalidArgument("Invalid time format, missing ':' in '{}'",
time_str.to_string());
}

std::string hours_sub = time_hour_str.substring(0, colon1).to_string();
int hours = StringParser::string_to_int_internal<int32_t, true>(
hours_sub.data(), hours_sub.size(), &success);
if (success != StringParser::PARSE_SUCCESS) {
return Status::InvalidArgument("Invalid hours format in '{}'",
time_str.to_string());
}

std::string minutes_sub =
time_hour_str.substring(colon1 + 1, colon2 - colon1 - 1).to_string();
int minutes = StringParser::string_to_int_internal<int32_t, true>(
minutes_sub.data(), minutes_sub.size(), &success);
if (success != StringParser::PARSE_SUCCESS || minutes >= 60) {
return Status::InvalidArgument("Invalid minutes format in '{}'",
time_str.to_string());
}

std::string seconds_sub = time_hour_str.substring(colon2 + 1).to_string();
int seconds = StringParser::string_to_int_internal<int32_t, true>(
seconds_sub.data(), seconds_sub.size(), &success);
if (success != StringParser::PARSE_SUCCESS || seconds >= 60) {
return Status::InvalidArgument("Invalid seconds format in '{}'",
time_str.to_string());
}

int delta = days * 24 * 3600 + hours * 3600 + minutes * 60 + seconds;

Op::vector_constant(sources->get_data(), res_col->get_data(), delta, nullmap0,
nullmap1);
} else {
const auto col1_inside_const = assert_cast<const IntervalColumnType&>(
nest_col1_const->get_data_column());
Op::vector_constant(sources->get_data(), res_col->get_data(),
col1_inside_const.get_data()[0], nullmap0, nullmap1);
}
} else { // vector-vector
const auto concrete_col1 = assert_cast<const IntervalColumnType&>(*nest_col1);
Op::vector_vector(sources->get_data(), concrete_col1.get_data(),
Expand Down Expand Up @@ -855,7 +954,8 @@ struct CurrentTimeImpl {
dtv.microsecond());
} else {
return Status::InvalidArgument(
"The precision in function CURTIME should be between 0 and 6, but got {}",
"The precision in function CURTIME should be between 0 and 6, but got "
"{}",
precision);
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ using FunctionAddWeeksV2 = FunctionDateOrDateTimeComputation<AddWeeksImpl<TYPE_D
using FunctionAddMonthsV2 = FunctionDateOrDateTimeComputation<AddMonthsImpl<TYPE_DATEV2>>;
using FunctionAddQuartersV2 = FunctionDateOrDateTimeComputation<AddQuartersImpl<TYPE_DATEV2>>;
using FunctionAddYearsV2 = FunctionDateOrDateTimeComputation<AddYearsImpl<TYPE_DATEV2>>;
using FunctionAddDaySecondV2 = FunctionDateOrDateTimeComputation<AddDaySecondImpl<TYPE_DATEV2>>;
Copy link
Contributor

Choose a reason for hiding this comment

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

you dont have this signature, please remove


using FunctionSubDaysV2 = FunctionDateOrDateTimeComputation<SubtractDaysImpl<TYPE_DATEV2>>;
using FunctionSubWeeksV2 = FunctionDateOrDateTimeComputation<SubtractWeeksImpl<TYPE_DATEV2>>;
Expand Down Expand Up @@ -53,6 +54,8 @@ using FunctionDatetimeV2AddMonths =
using FunctionDatetimeV2AddQuarters =
FunctionDateOrDateTimeComputation<AddQuartersImpl<TYPE_DATETIMEV2>>;
using FunctionDatetimeV2AddYears = FunctionDateOrDateTimeComputation<AddYearsImpl<TYPE_DATETIMEV2>>;
using FunctionDatetimeV2AddDaySecond =
FunctionDateOrDateTimeComputation<AddDaySecondImpl<TYPE_DATETIMEV2>>;

using FunctionDatetimeV2SubMicroseconds =
FunctionDateOrDateTimeComputation<SubtractMicrosecondsImpl<TYPE_DATETIMEV2>>;
Expand Down Expand Up @@ -105,6 +108,7 @@ void register_function_date_time_computation_v2(SimpleFunctionFactory& factory)
factory.register_function<FunctionAddMonthsV2>();
factory.register_function<FunctionAddYearsV2>();
factory.register_function<FunctionAddQuartersV2>();
factory.register_function<FunctionAddDaySecondV2>();

factory.register_function<FunctionDatetimeV2AddMicroseconds>();
factory.register_function<FunctionDatetimeV2AddMilliseconds>();
Expand All @@ -116,6 +120,7 @@ void register_function_date_time_computation_v2(SimpleFunctionFactory& factory)
factory.register_function<FunctionDatetimeV2AddMonths>();
factory.register_function<FunctionDatetimeV2AddYears>();
factory.register_function<FunctionDatetimeV2AddQuarters>();
factory.register_function<FunctionDatetimeV2AddDaySecond>();

factory.register_function<FunctionSubDaysV2>();
factory.register_function<FunctionSubMonthsV2>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ DATEV2: 'DATEV2';
DATETIMEV1: 'DATETIMEV1';
DATEV1: 'DATEV1';
DAY: 'DAY';
DAY_SECOND: 'DAY_SECOND';
DAYS: 'DAYS';
DECIMAL: 'DECIMAL';
DECIMALV2: 'DECIMALV2';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1739,7 +1739,7 @@ interval
;

unitIdentifier
: YEAR | QUARTER | MONTH | WEEK | DAY | HOUR | MINUTE | SECOND
: YEAR | QUARTER | MONTH | WEEK | DAY | HOUR | MINUTE | SECOND | DAY_SECOND
;

dataTypeWithNullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.apache.doris.nereids.trees.expressions.functions.scalar.DateDiff;
import org.apache.doris.nereids.trees.expressions.functions.scalar.DayCeil;
import org.apache.doris.nereids.trees.expressions.functions.scalar.DayFloor;
import org.apache.doris.nereids.trees.expressions.functions.scalar.DaySecondAdd;
import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysAdd;
import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysDiff;
import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysSub;
Expand Down Expand Up @@ -299,6 +300,8 @@ private Expression processDateAdd(TimeUnit unit, Expression timestamp, Expressio
return new MinutesAdd(timestamp, amount);
case SECOND:
return new SecondsAdd(timestamp, amount);
case DAY_SECOND:
return new DaySecondAdd(timestamp, amount);
default:
throw new AnalysisException("Unsupported time stamp add time unit: " + unit
+ ", supported time unit: YEAR/QUARTER/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.apache.doris.nereids.trees.expressions.literal.DateV2Literal;
import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
import org.apache.doris.nereids.trees.expressions.literal.TimeV2Literal;
import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
Expand Down Expand Up @@ -125,6 +126,14 @@ public static Expression daysAdd(DateTimeV2Literal date, IntegerLiteral day) {
return date.plusDays(day.getValue());
}

/**
* datetime arithmetic function day_second-add.
*/
@ExecFunction(name = "day_second_add")
public static Expression daysAdd(DateTimeV2Literal date, VarcharLiteral daySecond) {
return date.plusDaySecond(daySecond);
}

/**
* datetime arithmetic function hours-add.
*/
Expand Down
Loading