blob: 4cf0f6af85e2dc510ff9edd066e2a048589cbdbe [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.
#include <glog/logging.h>
#include <algorithm>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstring>
#if !defined(__APPLE__)
#include <experimental/bits/simd.h>
#endif
#include <memory>
#include <type_traits>
#include <utility>
#include "common/compiler_util.h"
#include "common/status.h"
#include "runtime/define_primitive_type.h"
#include "util/binary_cast.hpp"
#include "vec/aggregate_functions/aggregate_function.h"
#include "vec/columns/column.h"
#include "vec/columns/column_const.h"
#include "vec/columns/column_nullable.h"
#include "vec/columns/column_vector.h"
#include "vec/common/assert_cast.h"
#include "vec/common/pod_array_fwd.h"
#include "vec/core/block.h"
#include "vec/core/column_numbers.h"
#include "vec/core/column_with_type_and_name.h"
#include "vec/core/field.h"
#include "vec/core/types.h"
#include "vec/data_types/data_type.h"
#include "vec/data_types/data_type_date_or_datetime_v2.h"
#include "vec/data_types/data_type_date_time.h"
#include "vec/data_types/data_type_nullable.h"
#include "vec/data_types/data_type_number.h"
#include "vec/functions/datetime_errors.h"
#include "vec/functions/function.h"
#include "vec/functions/simple_function_factory.h"
#include "vec/runtime/vdatetime_value.h"
#include "vec/utils/util.hpp"
namespace doris {
#include "common/compile_check_begin.h"
class FunctionContext;
} // namespace doris
// FIXME: This file contains widespread UB due to unsafe type-punning casts.
// These must be properly refactored to eliminate reliance on reinterpret-style behavior.
//
// Temporarily suppress GCC 15+ warnings on user-defined type casts to allow build to proceed.
#if defined(__GNUC__) && (__GNUC__ >= 15)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-user-defined"
#endif
namespace doris::vectorized {
struct DayCeil;
struct DayFloor;
struct HourCeil;
struct HourFloor;
struct MinuteCeil;
struct MinuteFloor;
struct MonthCeil;
struct MonthFloor;
struct SecondCeil;
struct SecondFloor;
struct WeekCeil;
struct WeekFloor;
struct YearCeil;
struct YearFloor;
#if (defined(FLOOR) || defined(CEIL))
#error "FLOOR or CEIL is already defined"
#else
#define FLOOR 0
#define CEIL 1
#endif
template <typename Flag, PrimitiveType PType, int ArgNum, bool UseDelta = false>
class FunctionDateTimeFloorCeil : public IFunction {
public:
using DateType = PrimitiveTypeTraits<PType>::DataType;
using DateValueType = PrimitiveTypeTraits<PType>::CppType;
using NativeType = PrimitiveTypeTraits<PType>::CppNativeType;
using DeltaDataType = DataTypeInt32;
// return date type = DateType
static constexpr auto name = Flag::name;
static FunctionPtr create() { return std::make_shared<FunctionDateTimeFloorCeil>(); }
String get_name() const override { return name; }
size_t get_number_of_arguments() const override { return 0; }
bool is_variadic() const override { return true; }
bool use_default_implementation_for_nulls() const override { return false; }
DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
return have_nullable(arguments) ? make_nullable(std::make_shared<DateType>())
: std::make_shared<DateType>();
}
DataTypes get_variadic_argument_types_impl() const override {
if constexpr (ArgNum == 1) {
return {std::make_shared<DateType>()};
} else if constexpr (ArgNum == 2) {
if constexpr (UseDelta) {
return {std::make_shared<DateType>(), std::make_shared<DeltaDataType>()};
} else {
return {std::make_shared<DateType>(), std::make_shared<DateType>()};
}
}
// 3 args. both delta and origin.
return {std::make_shared<DateType>(), std::make_shared<DeltaDataType>(),
std::make_shared<DateType>()};
}
Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
uint32_t result, size_t input_rows_count) const override {
// Handle null map manually - update result null map from input null maps upfront
auto result_null_map_column = ColumnUInt8::create(input_rows_count, 0);
NullMap& result_null_map = assert_cast<ColumnUInt8&>(*result_null_map_column).get_data();
ColumnPtr argument_columns[3];
bool col_const[3];
// Update result null map from all input null maps using standard approach
for (int i = 0; i < arguments.size(); ++i) {
ColumnPtr& col = block.get_by_position(arguments[i]).column;
col_const[i] = is_column_const(*col);
const NullMap* null_map = VectorizedUtils::get_null_map(col);
if (null_map) {
VectorizedUtils::update_null_map(result_null_map, *null_map, col_const[i]);
}
}
// Extract nested columns from const(nullable) wrappers
argument_columns[0] = col_const[0] ? static_cast<const ColumnConst&>(
*block.get_by_position(arguments[0]).column)
.convert_to_full_column()
: block.get_by_position(arguments[0]).column;
argument_columns[0] = remove_nullable(argument_columns[0]);
if constexpr (ArgNum == 3) {
default_preprocess_parameter_columns(argument_columns, col_const, {1, 2}, block,
arguments);
argument_columns[1] = remove_nullable(argument_columns[1]);
argument_columns[2] = remove_nullable(argument_columns[2]);
} else if constexpr (ArgNum == 2) {
default_preprocess_parameter_columns(argument_columns, col_const, {1}, block,
arguments);
argument_columns[1] = remove_nullable(argument_columns[1]);
} else {
static_assert(ArgNum == 1);
}
const auto* sources = assert_cast<const ColumnVector<PType>*>(
argument_columns[0].get()); // datetime or date column
auto col_to = ColumnVector<PType>::create();
col_to->resize(input_rows_count);
if constexpr (ArgNum == 1) {
vector(sources->get_data(), col_to->get_data(), result_null_map);
} else if constexpr (ArgNum == 2) {
const IColumn& delta_column = *argument_columns[1];
if (col_const[1]) {
if (remove_nullable(block.get_by_position(arguments[1]).type)
->get_primitive_type() == PrimitiveType::TYPE_INT) {
// time_round(datetime,const(period))
Int32 period = (*argument_columns[1])[0].get<Int32>();
bool period_is_null = block.get_by_position(arguments[1]).type->is_nullable() &&
block.get_by_position(arguments[1]).column->is_null_at(0);
if (period < 1 && !period_is_null) [[unlikely]] {
throw_out_of_bound_int(Flag::name, period);
}
vector_const_period(sources->get_data(), period, col_to->get_data(),
result_null_map);
} else {
// time_round(datetime, const(origin))
vector_const_anchor(sources->get_data(),
(*argument_columns[1])[0].get<NativeType>(),
col_to->get_data(), result_null_map);
}
} else {
if (const auto* delta_vec_column0 =
check_and_get_column<ColumnVector<PType>>(delta_column)) {
// time_round(datetime, origin)
vector_vector_anchor(sources->get_data(), delta_vec_column0->get_data(),
col_to->get_data(), result_null_map);
} else {
const auto* delta_vec_column1 = check_and_get_column<ColumnInt32>(delta_column);
DCHECK(delta_vec_column1 != nullptr);
// time_round(datetime, period)
vector_vector_period(sources->get_data(), delta_vec_column1->get_data(),
col_to->get_data(), result_null_map);
}
}
} else { // 3 arg, time_round(datetime, period, origin)
if (col_const[1] && col_const[2]) {
// time_round(datetime, const(period), const(origin))
Int32 period = (*argument_columns[1])[0].get<Int32>();
NativeType origin = (*argument_columns[2])[0].get<NativeType>();
bool period_is_null = block.get_by_position(arguments[1]).type->is_nullable() &&
block.get_by_position(arguments[1]).column->is_null_at(0);
if (period < 1 && !period_is_null) [[unlikely]] {
throw_out_of_bound_int(Flag::name, period);
}
vector_const_const(sources->get_data(), period, origin, col_to->get_data(),
result_null_map);
} else if (col_const[1] && !col_const[2]) {
const auto arg2_column =
check_and_get_column<ColumnVector<PType>>(*argument_columns[2]);
// time_round(datetime, const(period), origin)
Int32 period = (*argument_columns[1])[0].get<Int32>();
bool period_is_null = block.get_by_position(arguments[1]).type->is_nullable() &&
block.get_by_position(arguments[1]).column->is_null_at(0);
if (period < 1 && !period_is_null) [[unlikely]] {
throw_out_of_bound_int(Flag::name, period);
}
vector_const_vector(sources->get_data(), period, arg2_column->get_data(),
col_to->get_data(), result_null_map);
} else if (!col_const[1] && col_const[2]) {
const auto* arg1_column = check_and_get_column<ColumnInt32>(*argument_columns[1]);
// time_round(datetime, period, const(origin))
vector_vector_const(sources->get_data(), arg1_column->get_data(),
(*argument_columns[2])[0].get<NativeType>(), col_to->get_data(),
result_null_map);
} else {
const auto* arg1_column = check_and_get_column<ColumnInt32>(*argument_columns[1]);
const auto arg2_column =
check_and_get_column<ColumnVector<PType>>(*argument_columns[2]);
DCHECK(arg1_column != nullptr);
DCHECK(arg2_column != nullptr);
// time_round(datetime, period, origin)
vector_vector_vector(sources->get_data(), arg1_column->get_data(),
arg2_column->get_data(), col_to->get_data(), result_null_map);
}
}
if (block.get_by_position(result).type->is_nullable()) {
block.replace_by_position(
result,
ColumnNullable::create(std::move(col_to), std::move(result_null_map_column)));
} else {
block.replace_by_position(result, std::move(col_to));
}
return Status::OK();
}
private:
static void vector(const PaddedPODArray<NativeType>& dates, PaddedPODArray<NativeType>& res,
const NullMap& result_null_map) {
for (int i = 0; i < dates.size(); ++i) {
if (result_null_map[i]) {
continue;
}
if (!time_round_reinterpret_two_args(dates[i], 1, res[i])) {
throw_out_of_bound_one_date<DateValueType>(Flag::name, dates[i]);
}
}
}
static void vector_const_anchor(const PaddedPODArray<NativeType>& dates, NativeType origin_date,
PaddedPODArray<NativeType>& res,
const NullMap& result_null_map) {
for (int i = 0; i < dates.size(); ++i) {
if (result_null_map[i]) {
continue;
}
if (!time_round_reinterpret_three_args(dates[i], 1, origin_date, res[i])) {
throw_out_of_bound_date_date<DateValueType>(Flag::name, dates[i], origin_date);
}
}
}
static void vector_const_period(const PaddedPODArray<NativeType>& dates, Int32 period,
PaddedPODArray<NativeType>& res,
const NullMap& result_null_map) {
// expand codes for const input periods
#define EXPAND_CODE_FOR_CONST_INPUT(X) \
case X: { \
for (int i = 0; i < dates.size(); ++i) { \
if (result_null_map[i]) { \
continue; \
} \
if (!time_round_reinterpret_two_args<X>(dates[i], period, res[i])) { \
throw_out_of_bound_date_int<DateValueType>(Flag::name, dates[i], period); \
} \
} \
return; \
}
#define EXPANDER(z, n, text) EXPAND_CODE_FOR_CONST_INPUT(n)
switch (period) {
// expand for some constant period
BOOST_PP_REPEAT(12, EXPANDER, ~)
default:
for (int i = 0; i < dates.size(); ++i) {
if (result_null_map[i]) {
continue;
}
if (!time_round_reinterpret_two_args(dates[i], period, res[i])) {
throw_out_of_bound_date_int<DateValueType>(Flag::name, dates[i], period);
}
}
}
#undef EXPAND_CODE_FOR_CONST_INPUT
#undef EXPANDER
}
static void vector_const_const(const PaddedPODArray<NativeType>& dates, const Int32 period,
NativeType origin_date, PaddedPODArray<NativeType>& res,
const NullMap& result_null_map) {
if (auto cast_date = binary_cast<NativeType, DateValueType>(origin_date);
cast_date == DateValueType::FIRST_DAY) {
vector_const_period(dates, period, res, result_null_map);
return;
}
// expand codes for const input periods
#define EXPAND_CODE_FOR_CONST_INPUT(X) \
case X: { \
for (int i = 0; i < dates.size(); ++i) { \
if (result_null_map[i]) { \
continue; \
} \
/* expand time_round_reinterpret_three_args*/ \
res[i] = origin_date; \
auto ts2 = binary_cast<NativeType, DateValueType>(dates[i]); \
auto& ts1 = (DateValueType&)(res[i]); \
if (!time_round_two_args<X>(ts2, X, ts1)) { \
throw_out_of_bound_int_date<DateValueType>(Flag::name, dates[i], period, \
origin_date); \
} \
} \
return; \
}
#define EXPANDER(z, n, text) EXPAND_CODE_FOR_CONST_INPUT(n)
switch (period) {
// expand for some constant period
BOOST_PP_REPEAT(12, EXPANDER, ~)
default:
for (int i = 0; i < dates.size(); ++i) {
if (result_null_map[i]) {
continue;
}
// always inline here
if (!time_round_reinterpret_three_args(dates[i], period, origin_date, res[i])) {
throw_out_of_bound_int_date<DateValueType>(Flag::name, dates[i], period,
origin_date);
}
}
}
#undef EXPAND_CODE_FOR_CONST_INPUT
#undef EXPANDER
}
static void vector_const_vector(const PaddedPODArray<NativeType>& dates, const Int32 period,
const PaddedPODArray<NativeType>& origin_dates,
PaddedPODArray<NativeType>& res,
const NullMap& result_null_map) {
for (int i = 0; i < dates.size(); ++i) {
if (result_null_map[i]) {
continue;
}
if (!time_round_reinterpret_three_args(dates[i], period, origin_dates[i], res[i])) {
throw_out_of_bound_int_date<DateValueType>(Flag::name, dates[i], period,
origin_dates[i]);
}
}
}
static void vector_vector_const(const PaddedPODArray<NativeType>& dates,
const PaddedPODArray<Int32>& periods, NativeType origin_date,
PaddedPODArray<NativeType>& res,
const NullMap& result_null_map) {
for (int i = 0; i < dates.size(); ++i) {
if (result_null_map[i]) {
continue;
}
if (periods[i] < 1) [[unlikely]] {
throw_out_of_bound_int_date<DateValueType>(Flag::name, dates[i], periods[i],
origin_date);
}
if (!time_round_reinterpret_three_args(dates[i], periods[i], origin_date, res[i])) {
throw_out_of_bound_int_date<DateValueType>(Flag::name, dates[i], periods[i],
origin_date);
}
}
}
static void vector_vector_anchor(const PaddedPODArray<NativeType>& dates,
const PaddedPODArray<NativeType>& origin_dates,
PaddedPODArray<NativeType>& res,
const NullMap& result_null_map) {
// time_round(datetime, origin)
for (int i = 0; i < dates.size(); ++i) {
if (result_null_map[i]) {
continue;
}
if (!time_round_reinterpret_three_args(dates[i], 1, origin_dates[i], res[i])) {
throw_out_of_bound_date_date<DateValueType>(Flag::name, dates[i], origin_dates[i]);
}
}
}
static void vector_vector_period(const PaddedPODArray<NativeType>& dates,
const PaddedPODArray<Int32>& periods,
PaddedPODArray<NativeType>& res,
const NullMap& result_null_map) {
// time_round(datetime, period)
for (int i = 0; i < dates.size(); ++i) {
if (result_null_map[i]) {
continue;
}
if (periods[i] < 1) [[unlikely]] {
throw_out_of_bound_date_int<DateValueType>(Flag::name, dates[i], periods[i]);
}
if (!time_round_reinterpret_two_args(dates[i], periods[i], res[i])) {
throw_out_of_bound_date_int<DateValueType>(Flag::name, dates[i], periods[i]);
}
}
}
static void vector_vector_vector(const PaddedPODArray<NativeType>& dates,
const PaddedPODArray<Int32>& periods,
const PaddedPODArray<NativeType>& origin_dates,
PaddedPODArray<NativeType>& res,
const NullMap& result_null_map) {
// time_round(datetime, period, origin)
for (int i = 0; i < dates.size(); ++i) {
if (result_null_map[i]) {
continue;
}
if (periods[i] < 1) [[unlikely]] {
throw_out_of_bound_int_date<DateValueType>(Flag::name, dates[i], periods[i],
origin_dates[i]);
}
if (!time_round_reinterpret_three_args(dates[i], periods[i], origin_dates[i], res[i])) {
throw_out_of_bound_int_date<DateValueType>(Flag::name, dates[i], periods[i],
origin_dates[i]);
}
}
}
//// time rounds
static constexpr uint32_t MASK_YEAR_FOR_DATEV2 = ((uint32_t)-1) >> 23;
static constexpr uint32_t BASE_MONTH_FOR_DATEV2 = 1U << 5;
static constexpr uint32_t MASK_YEAR_MONTH_FOR_DATEV2 = ((uint32_t)-1) >> 27;
static constexpr uint64_t MASK_YEAR_FOR_DATETIMEV2 = ((uint64_t)-1) >> 18;
static constexpr uint64_t BASE_MONTH_FOR_DATETIMEV2 = 1ULL << 42;
static constexpr uint64_t MASK_YEAR_MONTH_FOR_DATETIMEV2 = ((uint64_t)-1) >> 22;
static constexpr uint64_t MASK_YEAR_MONTH_DAY_FOR_DATETIMEV2 = ((uint64_t)-1) >> 27;
static constexpr uint64_t MASK_YEAR_MONTH_DAY_HOUR_FOR_DATETIMEV2 = ((uint64_t)-1) >> 32;
static constexpr uint64_t MASK_YEAR_MONTH_DAY_HOUR_MINUTE_FOR_DATETIMEV2 = ((uint64_t)-1) >> 38;
/// time rounds interlayers
ALWAYS_INLINE static bool time_round_reinterpret_one_arg(NativeType date, NativeType& res) {
auto ts_arg = binary_cast<NativeType, DateValueType>(date);
auto& ts_res = (DateValueType&)(res);
if constexpr (Flag::Unit == WEEK) {
ts_res = DateValueType::FIRST_DAY;
return time_round_two_args(ts_arg, 1, ts_res);
} else {
return time_round_one_arg(ts_arg, ts_res);
}
}
template <int const_period = 0>
static bool time_round_reinterpret_two_args(NativeType date, Int32 period, NativeType& res) {
auto ts_arg = binary_cast<NativeType, DateValueType>(date);
auto& ts_res = (DateValueType&)(res);
if constexpr (const_period == 0) {
if (can_use_optimize(period)) {
floor_opt(ts_arg, ts_res, period);
return true;
} else {
ts_res = DateValueType::FIRST_DAY;
return time_round_two_args(ts_arg, period, ts_res);
}
} else {
if (can_use_optimize(const_period)) {
floor_opt(ts_arg, ts_res, const_period);
return true;
} else {
ts_res = DateValueType::FIRST_DAY;
return time_round_two_args<const_period>(ts_arg, const_period, ts_res);
}
}
}
ALWAYS_INLINE static bool time_round_reinterpret_three_args(NativeType date, Int32 period,
NativeType origin_date,
NativeType& res) {
res = origin_date;
auto ts2 = binary_cast<NativeType, DateValueType>(date);
auto& ts1 = (DateValueType&)(res);
return time_round_two_args(ts2, period, ts1);
}
/// time rounds real calculations
static bool time_round_one_arg(const DateValueType& ts_arg, DateValueType& ts_res) {
static_assert(Flag::Unit != WEEK);
if constexpr (can_use_optimize(1)) {
floor_opt_one_period(ts_arg, ts_res);
return true;
} else {
if constexpr (std::is_same_v<DateValueType, VecDateTimeValue>) {
ts_res.reset_zero_by_type(ts_arg.type());
}
int64_t diff;
bool part;
if constexpr (Flag::Unit == YEAR) {
diff = ts_arg.year();
part = (ts_arg.month() - 1) | (ts_arg.day() - 1) | ts_arg.hour() | ts_arg.minute() |
ts_arg.second() | ts_arg.microsecond();
}
if constexpr (Flag::Unit == QUARTER) {
// only ceil cannot be optimized then reach here.
diff = ts_arg.year() * 4 + ts_arg.quarter() - 1;
part = (ts_arg.month() - 1) % 3 | (ts_arg.day() - 1) | ts_arg.hour() |
ts_arg.minute() | ts_arg.second() | ts_arg.microsecond();
}
if constexpr (Flag::Unit == MONTH) {
diff = ts_arg.year() * 12 + ts_arg.month() - 1;
part = (ts_arg.day() - 1) | ts_arg.hour() | ts_arg.minute() | ts_arg.second() |
ts_arg.microsecond();
}
if constexpr (Flag::Unit == DAY) {
diff = ts_arg.daynr();
part = ts_arg.hour() | ts_arg.minute() | ts_arg.second() | ts_arg.microsecond();
}
if constexpr (Flag::Unit == HOUR) {
diff = ts_arg.daynr() * 24 + ts_arg.hour();
part = ts_arg.minute() | ts_arg.second() | ts_arg.microsecond();
}
if constexpr (Flag::Unit == MINUTE) {
diff = ts_arg.daynr() * 24L * 60 + ts_arg.hour() * 60 + ts_arg.minute();
part = ts_arg.second() | ts_arg.microsecond();
}
if constexpr (Flag::Unit == SECOND) {
diff = ts_arg.daynr() * 24L * 60 * 60 + ts_arg.hour() * 60L * 60 +
ts_arg.minute() * 60L + ts_arg.second();
part = ts_arg.microsecond();
}
if constexpr (Flag::Type == CEIL) {
if (part) {
diff++;
}
}
TimeInterval interval(Flag::Unit, diff, false);
return ts_res.template date_set_interval<Flag::Unit>(interval);
}
}
// ts_res should be initialized with the ts_origin.
template <Int32 const_period = 0>
static bool time_round_two_args(const DateValueType& ts_arg, const Int32 period,
DateValueType& ts_origin) {
DateValueType& ts_res = ts_origin;
int64_t diff;
int64_t trivial_part_ts_res;
int64_t trivial_part_ts_arg;
if constexpr (std::is_same_v<DateValueType, VecDateTimeValue>) {
if constexpr (Flag::Unit == YEAR) {
diff = (ts_arg.year() - ts_origin.year());
trivial_part_ts_arg = ts_arg.to_int64() % 10000000000;
trivial_part_ts_res = ts_origin.to_int64() % 10000000000;
}
if constexpr (Flag::Unit == QUARTER) {
diff = (ts_arg.year() - ts_origin.year()) * 4 +
(ts_arg.month() - ts_origin.month()) / 3;
trivial_part_ts_arg =
(ts_arg.month() - 1) % 3 * 100000000 + (ts_arg.to_int64() % 100000000);
trivial_part_ts_res = (ts_origin.month() - 1) % 3 * 100000000 +
(ts_origin.to_int64() % 100000000);
}
if constexpr (Flag::Unit == MONTH) {
diff = (ts_arg.year() - ts_origin.year()) * 12 +
(ts_arg.month() - ts_origin.month());
trivial_part_ts_arg = ts_arg.to_int64() % 100000000;
trivial_part_ts_res = ts_origin.to_int64() % 100000000;
}
if constexpr (Flag::Unit == WEEK) {
diff = ts_arg.daynr() / 7 - ts_origin.daynr() / 7;
trivial_part_ts_arg = ts_arg.daynr() % 7 * 24 * 3600 + ts_arg.hour() * 3600 +
ts_arg.minute() * 60 + ts_arg.second();
trivial_part_ts_res = ts_origin.daynr() % 7 * 24 * 3600 + ts_origin.hour() * 3600 +
ts_origin.minute() * 60 + ts_origin.second();
}
if constexpr (Flag::Unit == DAY) {
diff = ts_arg.daynr() - ts_origin.daynr();
trivial_part_ts_arg = ts_arg.hour() * 3600 + ts_arg.minute() * 60 + ts_arg.second();
trivial_part_ts_res =
ts_origin.hour() * 3600 + ts_origin.minute() * 60 + ts_origin.second();
}
if constexpr (Flag::Unit == HOUR) {
diff = (ts_arg.daynr() - ts_origin.daynr()) * 24 +
(ts_arg.hour() - ts_origin.hour());
trivial_part_ts_arg = ts_arg.minute() * 60 + ts_arg.second();
trivial_part_ts_res = ts_origin.minute() * 60 + ts_origin.second();
}
if constexpr (Flag::Unit == MINUTE) {
diff = (ts_arg.daynr() - ts_origin.daynr()) * 24 * 60 +
(ts_arg.hour() - ts_origin.hour()) * 60 +
(ts_arg.minute() - ts_origin.minute());
trivial_part_ts_arg = ts_arg.second();
trivial_part_ts_res = ts_origin.second();
}
if constexpr (Flag::Unit == SECOND) {
diff = ts_arg.datetime_diff_in_seconds(ts_origin);
trivial_part_ts_res = 0;
trivial_part_ts_arg = 0;
}
} else if constexpr (std::is_same_v<DateValueType, DateV2Value<DateV2ValueType>>) {
if constexpr (Flag::Unit == YEAR) {
diff = (ts_arg.year() - ts_origin.year());
trivial_part_ts_arg = ts_arg.to_date_int_val() & MASK_YEAR_FOR_DATEV2;
trivial_part_ts_res = ts_origin.to_date_int_val() & MASK_YEAR_FOR_DATEV2;
}
if constexpr (Flag::Unit == QUARTER) {
diff = (ts_arg.year() - ts_origin.year()) * 4 +
(ts_arg.month() - ts_origin.month()) / 3;
trivial_part_ts_arg = ((ts_arg.month() - 1) % 3) * BASE_MONTH_FOR_DATEV2 +
(ts_arg.to_date_int_val() & MASK_YEAR_MONTH_FOR_DATEV2);
trivial_part_ts_res = ((ts_origin.month() - 1) % 3) * BASE_MONTH_FOR_DATEV2 +
(ts_origin.to_date_int_val() & MASK_YEAR_MONTH_FOR_DATEV2);
}
if constexpr (Flag::Unit == MONTH) {
diff = (ts_arg.year() - ts_origin.year()) * 12 +
(ts_arg.month() - ts_origin.month());
trivial_part_ts_arg = ts_arg.to_date_int_val() & MASK_YEAR_MONTH_FOR_DATEV2;
trivial_part_ts_res = ts_origin.to_date_int_val() & MASK_YEAR_MONTH_FOR_DATEV2;
}
if constexpr (Flag::Unit == WEEK) {
diff = ts_arg.daynr() / 7 - ts_origin.daynr() / 7;
trivial_part_ts_arg = ts_arg.daynr() % 7 * 24 * 3600 + ts_arg.hour() * 3600 +
ts_arg.minute() * 60 + ts_arg.second();
trivial_part_ts_res = ts_origin.daynr() % 7 * 24 * 3600 + ts_origin.hour() * 3600 +
ts_origin.minute() * 60 + ts_origin.second();
}
if constexpr (Flag::Unit == DAY) {
diff = ts_arg.daynr() - ts_origin.daynr();
trivial_part_ts_arg = ts_arg.hour() * 3600 + ts_arg.minute() * 60 + ts_arg.second();
trivial_part_ts_res =
ts_origin.hour() * 3600 + ts_origin.minute() * 60 + ts_origin.second();
}
if constexpr (Flag::Unit == HOUR) {
diff = (ts_arg.daynr() - ts_origin.daynr()) * 24 +
(ts_arg.hour() - ts_origin.hour());
trivial_part_ts_arg = ts_arg.minute() * 60 + ts_arg.second();
trivial_part_ts_res = ts_origin.minute() * 60 + ts_origin.second();
}
if constexpr (Flag::Unit == MINUTE) {
diff = (ts_arg.daynr() - ts_origin.daynr()) * 24 * 60 +
(ts_arg.hour() - ts_origin.hour()) * 60 +
(ts_arg.minute() - ts_origin.minute());
trivial_part_ts_arg = ts_arg.second();
trivial_part_ts_res = ts_origin.second();
}
if constexpr (Flag::Unit == SECOND) {
diff = ts_arg.datetime_diff_in_seconds(ts_origin);
trivial_part_ts_res = 0;
trivial_part_ts_arg = 0;
}
} else if constexpr (std::is_same_v<DateValueType, DateV2Value<DateTimeV2ValueType>>) {
if constexpr (Flag::Unit == YEAR) {
diff = (ts_arg.year() - ts_origin.year());
trivial_part_ts_arg = ts_arg.to_date_int_val() & MASK_YEAR_FOR_DATETIMEV2;
trivial_part_ts_res = ts_origin.to_date_int_val() & MASK_YEAR_FOR_DATETIMEV2;
}
if constexpr (Flag::Unit == QUARTER) {
diff = (ts_arg.year() - ts_origin.year()) * 4 +
(ts_arg.month() - ts_origin.month()) / 3;
trivial_part_ts_arg = ((ts_arg.month() - 1) % 3) * BASE_MONTH_FOR_DATETIMEV2 +
(ts_arg.to_date_int_val() & MASK_YEAR_MONTH_FOR_DATETIMEV2);
trivial_part_ts_res =
((ts_origin.month() - 1) % 3) * BASE_MONTH_FOR_DATETIMEV2 +
(ts_origin.to_date_int_val() & MASK_YEAR_MONTH_FOR_DATETIMEV2);
}
if constexpr (Flag::Unit == MONTH) {
diff = (ts_arg.year() - ts_origin.year()) * 12 +
(ts_arg.month() - ts_origin.month());
trivial_part_ts_arg = ts_arg.to_date_int_val() & MASK_YEAR_MONTH_FOR_DATETIMEV2;
trivial_part_ts_res = ts_origin.to_date_int_val() & MASK_YEAR_MONTH_FOR_DATETIMEV2;
}
if constexpr (Flag::Unit == WEEK) {
diff = ts_arg.daynr() / 7 - ts_origin.daynr() / 7;
trivial_part_ts_arg = ts_arg.daynr() % 7 * 24 * 3600 + ts_arg.hour() * 3600 +
ts_arg.minute() * 60 + ts_arg.second();
trivial_part_ts_res = ts_origin.daynr() % 7 * 24 * 3600 + ts_origin.hour() * 3600 +
ts_origin.minute() * 60 + ts_origin.second();
}
if constexpr (Flag::Unit == DAY) {
diff = ts_arg.daynr() - ts_origin.daynr();
trivial_part_ts_arg = ts_arg.to_date_int_val() & MASK_YEAR_MONTH_DAY_FOR_DATETIMEV2;
trivial_part_ts_res =
ts_origin.to_date_int_val() & MASK_YEAR_MONTH_DAY_FOR_DATETIMEV2;
}
if constexpr (Flag::Unit == HOUR) {
diff = (ts_arg.daynr() - ts_origin.daynr()) * 24 +
(ts_arg.hour() - ts_origin.hour());
trivial_part_ts_arg =
ts_arg.to_date_int_val() & MASK_YEAR_MONTH_DAY_HOUR_FOR_DATETIMEV2;
trivial_part_ts_res =
ts_origin.to_date_int_val() & MASK_YEAR_MONTH_DAY_HOUR_FOR_DATETIMEV2;
}
if constexpr (Flag::Unit == MINUTE) {
diff = (ts_arg.daynr() - ts_origin.daynr()) * 24 * 60 +
(ts_arg.hour() - ts_origin.hour()) * 60 +
(ts_arg.minute() - ts_origin.minute());
trivial_part_ts_arg =
ts_arg.to_date_int_val() & MASK_YEAR_MONTH_DAY_HOUR_MINUTE_FOR_DATETIMEV2;
trivial_part_ts_res = ts_origin.to_date_int_val() &
MASK_YEAR_MONTH_DAY_HOUR_MINUTE_FOR_DATETIMEV2;
}
if constexpr (Flag::Unit == SECOND) {
diff = ts_arg.datetime_diff_in_seconds(ts_origin);
trivial_part_ts_arg = ts_arg.microsecond();
trivial_part_ts_res = ts_origin.microsecond();
}
}
//round down/up to specific time-unit(HOUR/DAY/MONTH...) by increase/decrease diff variable
if constexpr (Flag::Type == CEIL) {
//e.g. hour_ceil(ts: 00:00:40, origin: 00:00:30), ts should be rounded to 01:00:30
diff += trivial_part_ts_arg > trivial_part_ts_res;
}
if constexpr (Flag::Type == FLOOR) {
//e.g. hour_floor(ts: 01:00:20, origin: 00:00:30), ts should be rounded to 00:00:30
diff -= trivial_part_ts_arg < trivial_part_ts_res;
}
// round down/up inside time period(several time-units)
// e.g. if period is 3, diff is 8, then delta_inside_period is 2,
int64_t delta_inside_period;
if constexpr (const_period != 0) {
delta_inside_period = diff >= 0 ? diff % const_period
: (diff % const_period + const_period) % const_period;
} else {
delta_inside_period = diff >= 0 ? diff % period : (diff % period + period) % period;
}
int64_t step = diff - delta_inside_period +
(Flag::Type == FLOOR ? 0 : (delta_inside_period == 0 ? 0 : period));
bool is_neg = step < 0;
TimeInterval interval(Flag::Unit, std::abs(step), is_neg);
return ts_res.template date_add_interval<Flag::Unit>(interval);
}
/// optimized path
constexpr static bool can_use_optimize(int period) {
if constexpr (!std::is_same_v<DateValueType, VecDateTimeValue> && Flag::Type == FLOOR) {
if constexpr (Flag::Unit == YEAR || Flag::Unit == DAY || Flag::Unit == QUARTER) {
return period == 1;
}
if constexpr (Flag::Unit == MONTH) {
return period <= 11 && 12 % period == 0;
}
if constexpr (Flag::Unit == HOUR) {
return period <= 23 && 24 % period == 0;
}
if constexpr (Flag::Unit == MINUTE) {
return period <= 59 && 60 % period == 0;
}
if constexpr (Flag::Unit == SECOND) {
return period <= 59 && 60 % period == 0;
}
}
return false;
}
static void floor_opt(const DateValueType& ts2, DateValueType& ts1, int period) {
if (period == 1) {
// year, day, quarter must go through this path
floor_opt_one_period(ts2, ts1);
} else {
static constexpr uint64_t MASK_HOUR_FLOOR =
0b1111111111111111111111111111111100000000000000000000000000000000;
static constexpr uint64_t MASK_MINUTE_FLOOR =
0b1111111111111111111111111111111111111100000000000000000000000000;
static constexpr uint64_t MASK_SECOND_FLOOR =
0b1111111111111111111111111111111111111111111100000000000000000000;
// Optimize the performance of the datetimev2 type on the floor operation.
// Now supports unit month hour minute second. no need to check again when set value for ts
if constexpr (Flag::Unit == MONTH && !std::is_same_v<DateValueType, VecDateTimeValue>) {
int month = ts2.month() - 1;
int new_month = month / period * period;
if (new_month >= 12) {
new_month = new_month % 12;
}
ts1.unchecked_set_time(ts2.year(), ts2.month(), 1, 0, 0, 0);
ts1.template unchecked_set_time_unit<TimeUnit::MONTH>(new_month + 1);
}
if constexpr (Flag::Unit == HOUR && !std::is_same_v<DateValueType, VecDateTimeValue>) {
int hour = ts2.hour();
int new_hour = hour / period * period;
if (new_hour >= 24) {
new_hour = new_hour % 24;
}
ts1.set_int_val(ts2.to_date_int_val() & MASK_HOUR_FLOOR);
ts1.template unchecked_set_time_unit<TimeUnit::HOUR>(new_hour);
}
if constexpr (Flag::Unit == MINUTE &&
!std::is_same_v<DateValueType, VecDateTimeValue>) {
int minute = ts2.minute();
int new_minute = minute / period * period;
if (new_minute >= 60) {
new_minute = new_minute % 60;
}
ts1.set_int_val(ts2.to_date_int_val() & MASK_MINUTE_FLOOR);
ts1.template unchecked_set_time_unit<TimeUnit::MINUTE>(new_minute);
}
if constexpr (Flag::Unit == SECOND &&
!std::is_same_v<DateValueType, VecDateTimeValue>) {
int second = ts2.second();
int new_second = second / period * period;
if (new_second >= 60) {
new_second = new_second % 60;
}
ts1.set_int_val(ts2.to_date_int_val() & MASK_SECOND_FLOOR);
ts1.template unchecked_set_time_unit<TimeUnit::SECOND>(new_second);
}
}
}
static void floor_opt_one_period(const DateValueType& ts2, DateValueType& ts1) {
if constexpr (Flag::Unit == YEAR) {
ts1.unchecked_set_time(ts2.year(), 1, 1, 0, 0, 0);
}
if constexpr (Flag::Unit == QUARTER) {
// 1,4,7,10 are the first month of each quarter
if (ts2.month() <= 3) {
ts1.unchecked_set_time(ts2.year(), 1, 1, 0, 0, 0);
} else if (ts2.month() <= 6) {
ts1.unchecked_set_time(ts2.year(), 4, 1, 0, 0, 0);
} else if (ts2.month() <= 9) {
ts1.unchecked_set_time(ts2.year(), 7, 1, 0, 0, 0);
} else {
ts1.unchecked_set_time(ts2.year(), 10, 1, 0, 0, 0);
}
}
if constexpr (Flag::Unit == MONTH) {
ts1.unchecked_set_time(ts2.year(), ts2.month(), 1, 0, 0, 0);
}
if constexpr (Flag::Unit == DAY) {
ts1.unchecked_set_time(ts2.year(), ts2.month(), ts2.day(), 0, 0, 0);
}
// only DateTimeV2ValueType type have hour minute second
if constexpr (std::is_same_v<DateValueType, DateV2Value<DateTimeV2ValueType>>) {
static constexpr uint64_t MASK_HOUR_FLOOR =
0b1111111111111111111111111111111100000000000000000000000000000000;
static constexpr uint64_t MASK_MINUTE_FLOOR =
0b1111111111111111111111111111111111111100000000000000000000000000;
static constexpr uint64_t MASK_SECOND_FLOOR =
0b1111111111111111111111111111111111111111111100000000000000000000;
// Optimize the performance of the datetimev2 type on the floor operation.
// Now supports unit biger than SECOND
if constexpr (Flag::Unit == HOUR) {
ts1.set_int_val(ts2.to_date_int_val() & MASK_HOUR_FLOOR);
}
if constexpr (Flag::Unit == MINUTE) {
ts1.set_int_val(ts2.to_date_int_val() & MASK_MINUTE_FLOOR);
}
if constexpr (Flag::Unit == SECOND) {
ts1.set_int_val(ts2.to_date_int_val() & MASK_SECOND_FLOOR);
}
}
}
};
#define TIME_ROUND_WITH_DELTA_TYPE(IMPL, NAME, UNIT, TYPE, DELTA) \
using FunctionDateV2OneArg##IMPL##DELTA = FunctionDateTimeFloorCeil<IMPL, TYPE_DATEV2, 1>; \
using FunctionDateV2TwoArg##IMPL##DELTA = FunctionDateTimeFloorCeil<IMPL, TYPE_DATEV2, 2>; \
using FunctionDateV2ThreeArg##IMPL##DELTA = FunctionDateTimeFloorCeil<IMPL, TYPE_DATEV2, 3>; \
using FunctionDateTimeV2OneArg##IMPL##DELTA = \
FunctionDateTimeFloorCeil<IMPL, TYPE_DATETIMEV2, 1>; \
using FunctionDateTimeV2TwoArg##IMPL##DELTA = \
FunctionDateTimeFloorCeil<IMPL, TYPE_DATETIMEV2, 2>; \
using FunctionDateTimeV2ThreeArg##IMPL##DELTA = \
FunctionDateTimeFloorCeil<IMPL, TYPE_DATETIMEV2, 3>;
#define TIME_ROUND_DECLARE(IMPL, NAME, UNIT, TYPE) \
struct IMPL { \
static constexpr auto name = #NAME; \
static constexpr TimeUnit Unit = UNIT; \
static constexpr auto Type = TYPE; \
}; \
\
TIME_ROUND_WITH_DELTA_TYPE(IMPL, NAME, UNIT, TYPE, Int32) \
using FunctionDateV2TwoArg##IMPL = FunctionDateTimeFloorCeil<IMPL, TYPE_DATEV2, 2, true>; \
using FunctionDateTimeV2TwoArg##IMPL = \
FunctionDateTimeFloorCeil<IMPL, TYPE_DATETIMEV2, 2, true>;
TIME_ROUND_DECLARE(YearFloor, year_floor, YEAR, FLOOR);
TIME_ROUND_DECLARE(QuarterFloor, quarter_floor, QUARTER, FLOOR);
TIME_ROUND_DECLARE(MonthFloor, month_floor, MONTH, FLOOR);
TIME_ROUND_DECLARE(WeekFloor, week_floor, WEEK, FLOOR);
TIME_ROUND_DECLARE(DayFloor, day_floor, DAY, FLOOR);
TIME_ROUND_DECLARE(HourFloor, hour_floor, HOUR, FLOOR);
TIME_ROUND_DECLARE(MinuteFloor, minute_floor, MINUTE, FLOOR);
TIME_ROUND_DECLARE(SecondFloor, second_floor, SECOND, FLOOR);
TIME_ROUND_DECLARE(YearCeil, year_ceil, YEAR, CEIL);
TIME_ROUND_DECLARE(QuarterCeil, quarter_ceil, QUARTER, CEIL);
TIME_ROUND_DECLARE(MonthCeil, month_ceil, MONTH, CEIL);
TIME_ROUND_DECLARE(WeekCeil, week_ceil, WEEK, CEIL);
TIME_ROUND_DECLARE(DayCeil, day_ceil, DAY, CEIL);
TIME_ROUND_DECLARE(HourCeil, hour_ceil, HOUR, CEIL);
TIME_ROUND_DECLARE(MinuteCeil, minute_ceil, MINUTE, CEIL);
TIME_ROUND_DECLARE(SecondCeil, second_ceil, SECOND, CEIL);
void register_function_datetime_floor_ceil(SimpleFunctionFactory& factory) {
#define REGISTER_FUNC_WITH_DELTA_TYPE(IMPL, DELTA) \
factory.register_function<FunctionDateV2OneArg##IMPL##DELTA>(); \
factory.register_function<FunctionDateV2TwoArg##IMPL##DELTA>(); \
factory.register_function<FunctionDateV2ThreeArg##IMPL##DELTA>(); \
factory.register_function<FunctionDateTimeV2OneArg##IMPL##DELTA>(); \
factory.register_function<FunctionDateTimeV2TwoArg##IMPL##DELTA>(); \
factory.register_function<FunctionDateTimeV2ThreeArg##IMPL##DELTA>(); \
factory.register_function<FunctionDateTimeV2TwoArg##IMPL>(); \
factory.register_function<FunctionDateV2TwoArg##IMPL>();
#define REGISTER_FUNC(IMPL) REGISTER_FUNC_WITH_DELTA_TYPE(IMPL, Int32)
REGISTER_FUNC(YearFloor);
REGISTER_FUNC(QuarterFloor);
REGISTER_FUNC(MonthFloor);
REGISTER_FUNC(WeekFloor);
REGISTER_FUNC(DayFloor);
REGISTER_FUNC(HourFloor);
REGISTER_FUNC(MinuteFloor);
REGISTER_FUNC(SecondFloor);
REGISTER_FUNC(YearCeil);
REGISTER_FUNC(QuarterCeil);
REGISTER_FUNC(MonthCeil);
REGISTER_FUNC(WeekCeil);
REGISTER_FUNC(DayCeil);
REGISTER_FUNC(HourCeil);
REGISTER_FUNC(MinuteCeil);
REGISTER_FUNC(SecondCeil);
}
#undef FLOOR
#undef CEIL
} // namespace doris::vectorized
#if defined(__GNUC__) && (__GNUC__ >= 15)
#pragma GCC diagnostic pop
#endif