blob: 675af47a17195327fbf22e7692dfa4909768707c [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 "core/assert_cast.h"
#include "core/block/block.h"
#include "core/block/column_numbers.h"
#include "core/block/column_with_type_and_name.h"
#include "core/column/column.h"
#include "core/column/column_const.h"
#include "core/column/column_nullable.h"
#include "core/column/column_vector.h"
#include "core/data_type/data_type.h"
#include "core/data_type/data_type_date_or_datetime_v2.h"
#include "core/data_type/data_type_date_time.h"
#include "core/data_type/data_type_nullable.h"
#include "core/data_type/data_type_number.h"
#include "core/data_type/define_primitive_type.h"
#include "core/field.h"
#include "core/pod_array_fwd.h"
#include "core/types.h"
#include "core/value/timestamptz_value.h"
#include "core/value/vdatetime_value.h"
#include "exec/common/util.hpp"
#include "exprs/aggregate/aggregate_function.h"
#include "exprs/function/datetime_errors.h"
#include "exprs/function/function.h"
#include "exprs/function/simple_function_factory.h"
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 {
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
// UTC to local time conversion - only depends on DateValueType, not all template params
template <typename DateValueType>
void convert_utc_to_local_impl(const DateValueType& utc_val, DateValueType& local_val,
const cctz::time_zone& tz) {
cctz::time_point<cctz::sys_seconds> utc_tp =
cctz::convert(cctz::civil_second(utc_val.year(), utc_val.month(), utc_val.day(),
utc_val.hour(), utc_val.minute(), utc_val.second()),
cctz::utc_time_zone());
auto local_cs = cctz::convert(utc_tp, tz);
local_val.unchecked_set_time(
static_cast<uint16_t>(local_cs.year()), static_cast<uint8_t>(local_cs.month()),
static_cast<uint8_t>(local_cs.day()), static_cast<uint8_t>(local_cs.hour()),
static_cast<uint8_t>(local_cs.minute()), static_cast<uint8_t>(local_cs.second()),
utc_val.microsecond());
}
// Base class: non-template overrides shared by all instantiations
class FunctionDateTimeFloorCeilBase : public IFunction {
public:
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; }
};
// Forward declaration of core computation struct (only depends on Flag + PType)
template <typename Flag, PrimitiveType PType>
struct DateTimeFloorCeilCore;
template <typename Flag, PrimitiveType PType, int ArgNum, bool UseDelta = false>
class FunctionDateTimeFloorCeil : public FunctionDateTimeFloorCeilBase {
public:
using DateType = typename PrimitiveTypeTraits<PType>::DataType;
using DateValueType = typename PrimitiveTypeTraits<PType>::CppType;
using DeltaDataType = DataTypeInt32;
using Core = DateTimeFloorCeilCore<Flag, PType>;
// 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; }
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) {
Core::vector(sources->get_data(), col_to->get_data(), result_null_map, context);
} 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<TYPE_INT>();
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);
}
Core::vector_const_period(sources->get_data(), period, col_to->get_data(),
result_null_map, context);
} else {
// time_round(datetime, const(origin))
Core::vector_const_anchor(sources->get_data(),
(*argument_columns[1])[0].get<PType>(),
col_to->get_data(), result_null_map, context);
}
} else {
if (const auto* delta_vec_column0 =
check_and_get_column<ColumnVector<PType>>(delta_column)) {
// time_round(datetime, origin)
Core::vector_vector_anchor(sources->get_data(), delta_vec_column0->get_data(),
col_to->get_data(), result_null_map, context);
} else {
const auto* delta_vec_column1 = check_and_get_column<ColumnInt32>(delta_column);
DCHECK(delta_vec_column1 != nullptr);
// time_round(datetime, period)
Core::vector_vector_period(sources->get_data(), delta_vec_column1->get_data(),
col_to->get_data(), result_null_map, context);
}
}
} 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<TYPE_INT>();
auto origin = (*argument_columns[2])[0].get<PType>();
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);
}
Core::vector_const_const(sources->get_data(), period, origin, col_to->get_data(),
result_null_map, context);
} 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<TYPE_INT>();
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);
}
Core::vector_const_vector(sources->get_data(), period, arg2_column->get_data(),
col_to->get_data(), result_null_map, context);
} 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))
Core::vector_vector_const(sources->get_data(), arg1_column->get_data(),
(*argument_columns[2])[0].get<PType>(),
col_to->get_data(), result_null_map, context);
} 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)
Core::vector_vector_vector(sources->get_data(), arg1_column->get_data(),
arg2_column->get_data(), col_to->get_data(),
result_null_map, context);
}
}
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();
}
};
// Core computation struct - only depends on Flag + PType (not ArgNum/UseDelta).
// This reduces template instantiations from 192 to 48 for all heavy computation functions.
template <typename Flag, PrimitiveType PType>
struct DateTimeFloorCeilCore {
using DateValueType = typename PrimitiveTypeTraits<PType>::CppType;
static void vector(const PaddedPODArray<DateValueType>& dates,
PaddedPODArray<DateValueType>& res, const NullMap& result_null_map,
FunctionContext* context) {
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], context)) {
throw_out_of_bound_one_date<DateValueType>(Flag::name, dates[i]);
}
}
}
static void vector_const_anchor(const PaddedPODArray<DateValueType>& dates,
DateValueType origin_date, PaddedPODArray<DateValueType>& res,
const NullMap& result_null_map, FunctionContext* context) {
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], context)) {
throw_out_of_bound_date_date<DateValueType>(Flag::name, dates[i], origin_date);
}
}
}
static void vector_const_period(const PaddedPODArray<DateValueType>& dates, Int32 period,
PaddedPODArray<DateValueType>& res,
const NullMap& result_null_map, FunctionContext* context) {
// 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], context)) { \
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], context)) {
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<DateValueType>& dates, const Int32 period,
DateValueType origin_date, PaddedPODArray<DateValueType>& res,
const NullMap& result_null_map, FunctionContext* context) {
if (auto cast_date = origin_date; cast_date == DateValueType::FIRST_DAY) {
vector_const_period(dates, period, res, result_null_map, context);
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 = dates[i]; \
auto& ts1 = (DateValueType&)(res[i]); \
if (!time_round_two_args<X>(ts2, X, ts1, context)) { \
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],
context)) {
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<DateValueType>& dates, const Int32 period,
const PaddedPODArray<DateValueType>& origin_dates,
PaddedPODArray<DateValueType>& res,
const NullMap& result_null_map, FunctionContext* context) {
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],
context)) {
throw_out_of_bound_int_date<DateValueType>(Flag::name, dates[i], period,
origin_dates[i]);
}
}
}
static void vector_vector_const(const PaddedPODArray<DateValueType>& dates,
const PaddedPODArray<Int32>& periods, DateValueType origin_date,
PaddedPODArray<DateValueType>& res,
const NullMap& result_null_map, FunctionContext* context) {
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],
context)) {
throw_out_of_bound_int_date<DateValueType>(Flag::name, dates[i], periods[i],
origin_date);
}
}
}
static void vector_vector_anchor(const PaddedPODArray<DateValueType>& dates,
const PaddedPODArray<DateValueType>& origin_dates,
PaddedPODArray<DateValueType>& res,
const NullMap& result_null_map, FunctionContext* context) {
// 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], context)) {
throw_out_of_bound_date_date<DateValueType>(Flag::name, dates[i], origin_dates[i]);
}
}
}
static void vector_vector_period(const PaddedPODArray<DateValueType>& dates,
const PaddedPODArray<Int32>& periods,
PaddedPODArray<DateValueType>& res,
const NullMap& result_null_map, FunctionContext* context) {
// 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], context)) {
throw_out_of_bound_date_int<DateValueType>(Flag::name, dates[i], periods[i]);
}
}
}
static void vector_vector_vector(const PaddedPODArray<DateValueType>& dates,
const PaddedPODArray<Int32>& periods,
const PaddedPODArray<DateValueType>& origin_dates,
PaddedPODArray<DateValueType>& res,
const NullMap& result_null_map, FunctionContext* context) {
// 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],
context)) {
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;
template <int const_period = 0>
static bool time_round_reinterpret_two_args(DateValueType date, Int32 period,
DateValueType& res, FunctionContext* context) {
auto ts_arg = date;
auto& ts_res = 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, context);
}
} 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, context);
}
}
}
ALWAYS_INLINE static bool time_round_reinterpret_three_args(DateValueType date, Int32 period,
DateValueType origin_date,
DateValueType& res,
FunctionContext* context) {
res = origin_date;
auto ts2 = date;
return time_round_two_args(ts2, period, res, context);
}
// 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, FunctionContext* context) {
/* For TimestampTzValue on date-based units, convert UTC to local time first, eg:
* for YEAR_CEIL('2025-12-31 23:59:59+03:00') with time_zone: +08:00
* the local time is: '2026-01-01 04:59:59'
* We do ceil/floor opt based on the local time, which is '2026-01-01 00:00:00'
* before write back ans, we should sub a time_zone offset
* to make the ans is '2026-01-01 00:00:00+08:00' instead of '2026-01-01 08:00:00+08:00'
*/
constexpr bool need_tz_conversion =
std::is_same_v<DateValueType, TimestampTzValue> &&
(Flag::Unit == YEAR || Flag::Unit == QUARTER || Flag::Unit == MONTH ||
Flag::Unit == WEEK || Flag::Unit == DAY || Flag::Unit == HOUR ||
Flag::Unit == MINUTE);
DateValueType local_arg;
DateValueType local_origin;
const cctz::time_zone& tz = context->state()->timezone_obj();
if constexpr (need_tz_conversion) {
convert_utc_to_local_impl(ts_arg, local_arg, tz);
if (ts_origin == TimestampTzValue::FIRST_DAY) {
local_origin = ts_origin;
} else {
convert_utc_to_local_impl(ts_origin, local_origin, tz);
}
}
// Use local time for calculation if timezone conversion is needed
const DateValueType& calc_arg = need_tz_conversion ? local_arg : ts_arg;
const DateValueType& calc_origin = need_tz_conversion ? local_origin : ts_origin;
DateValueType& ts_res = need_tz_conversion ? local_origin : 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>> ||
std::is_same_v<DateValueType, TimestampTzValue>) {
if constexpr (Flag::Unit == YEAR) {
diff = (calc_arg.year() - calc_origin.year());
trivial_part_ts_arg = calc_arg.to_date_int_val() & MASK_YEAR_FOR_DATETIMEV2;
trivial_part_ts_res = calc_origin.to_date_int_val() & MASK_YEAR_FOR_DATETIMEV2;
}
if constexpr (Flag::Unit == QUARTER) {
diff = (calc_arg.year() - calc_origin.year()) * 4 +
(calc_arg.month() - calc_origin.month()) / 3;
trivial_part_ts_arg = ((calc_arg.month() - 1) % 3) * BASE_MONTH_FOR_DATETIMEV2 +
(calc_arg.to_date_int_val() & MASK_YEAR_MONTH_FOR_DATETIMEV2);
trivial_part_ts_res =
((calc_origin.month() - 1) % 3) * BASE_MONTH_FOR_DATETIMEV2 +
(calc_origin.to_date_int_val() & MASK_YEAR_MONTH_FOR_DATETIMEV2);
}
if constexpr (Flag::Unit == MONTH) {
diff = (calc_arg.year() - calc_origin.year()) * 12 +
(calc_arg.month() - calc_origin.month());
trivial_part_ts_arg = calc_arg.to_date_int_val() & MASK_YEAR_MONTH_FOR_DATETIMEV2;
trivial_part_ts_res =
calc_origin.to_date_int_val() & MASK_YEAR_MONTH_FOR_DATETIMEV2;
}
if constexpr (Flag::Unit == WEEK) {
diff = calc_arg.daynr() / 7 - calc_origin.daynr() / 7;
trivial_part_ts_arg = calc_arg.daynr() % 7 * 24 * 3600 + calc_arg.hour() * 3600 +
calc_arg.minute() * 60 + calc_arg.second();
trivial_part_ts_res = calc_origin.daynr() % 7 * 24 * 3600 +
calc_origin.hour() * 3600 + calc_origin.minute() * 60 +
calc_origin.second();
}
if constexpr (Flag::Unit == DAY) {
diff = calc_arg.daynr() - calc_origin.daynr();
trivial_part_ts_arg =
calc_arg.to_date_int_val() & MASK_YEAR_MONTH_DAY_FOR_DATETIMEV2;
trivial_part_ts_res =
calc_origin.to_date_int_val() & MASK_YEAR_MONTH_DAY_FOR_DATETIMEV2;
}
if constexpr (Flag::Unit == HOUR) {
diff = (calc_arg.daynr() - calc_origin.daynr()) * 24 +
(calc_arg.hour() - calc_origin.hour());
trivial_part_ts_arg =
calc_arg.to_date_int_val() & MASK_YEAR_MONTH_DAY_HOUR_FOR_DATETIMEV2;
trivial_part_ts_res =
calc_origin.to_date_int_val() & MASK_YEAR_MONTH_DAY_HOUR_FOR_DATETIMEV2;
}
if constexpr (Flag::Unit == MINUTE) {
diff = (calc_arg.daynr() - calc_origin.daynr()) * 24 * 60 +
(calc_arg.hour() - calc_origin.hour()) * 60 +
(calc_arg.minute() - calc_origin.minute());
trivial_part_ts_arg =
calc_arg.to_date_int_val() & MASK_YEAR_MONTH_DAY_HOUR_MINUTE_FOR_DATETIMEV2;
trivial_part_ts_res = calc_origin.to_date_int_val() &
MASK_YEAR_MONTH_DAY_HOUR_MINUTE_FOR_DATETIMEV2;
}
if constexpr (Flag::Unit == SECOND) {
diff = calc_arg.datetime_diff_in_seconds(calc_origin);
trivial_part_ts_arg = calc_arg.microsecond();
trivial_part_ts_res = calc_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);
bool result = ts_res.template date_add_interval<Flag::Unit>(interval);
// For TimestampTzValue on date-based units, convert result from local time back to UTC
if constexpr (need_tz_conversion) {
if (result) {
cctz::civil_second local_result_cs(ts_res.year(), ts_res.month(), ts_res.day(),
ts_res.hour(), ts_res.minute(), ts_res.second());
cctz::time_point<cctz::sys_seconds> local_tp = cctz::convert(local_result_cs, tz);
auto utc_result_cs = cctz::convert(local_tp, cctz::utc_time_zone());
ts_origin.unchecked_set_time(static_cast<uint16_t>(utc_result_cs.year()),
static_cast<uint8_t>(utc_result_cs.month()),
static_cast<uint8_t>(utc_result_cs.day()),
static_cast<uint8_t>(utc_result_cs.hour()),
static_cast<uint8_t>(utc_result_cs.minute()),
static_cast<uint8_t>(utc_result_cs.second()),
ts_res.microsecond());
}
}
return result;
}
/// optimized path
constexpr static bool can_use_optimize(int period) {
// For TimestampTzValue on date-based units, disable optimization to ensure timezone conversion
if constexpr (std::is_same_v<DateValueType, TimestampTzValue>) {
if constexpr (Flag::Unit == YEAR || Flag::Unit == QUARTER || Flag::Unit == MONTH ||
Flag::Unit == WEEK || Flag::Unit == DAY || Flag::Unit == HOUR ||
Flag::Unit == MINUTE) {
return false;
}
}
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 and TimestampTzValue types have hour minute second
if constexpr (std::is_same_v<DateValueType, DateV2Value<DateTimeV2ValueType>> ||
std::is_same_v<DateValueType, TimestampTzValue>) {
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>; \
using FunctionTimestamptzOneArg##IMPL##DELTA = \
FunctionDateTimeFloorCeil<IMPL, TYPE_TIMESTAMPTZ, 1>; \
using FunctionTimestamptzTwoArg##IMPL##DELTA = \
FunctionDateTimeFloorCeil<IMPL, TYPE_TIMESTAMPTZ, 2>; \
using FunctionTimestamptzThreeArg##IMPL##DELTA = \
FunctionDateTimeFloorCeil<IMPL, TYPE_TIMESTAMPTZ, 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>; \
using FunctionTimestamptzTwoArg##IMPL = \
FunctionDateTimeFloorCeil<IMPL, TYPE_TIMESTAMPTZ, 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<FunctionTimestamptzOneArg##IMPL##DELTA>(); \
factory.register_function<FunctionTimestamptzTwoArg##IMPL##DELTA>(); \
factory.register_function<FunctionTimestamptzThreeArg##IMPL##DELTA>(); \
factory.register_function<FunctionDateTimeV2TwoArg##IMPL>(); \
factory.register_function<FunctionDateV2TwoArg##IMPL>(); \
factory.register_function<FunctionTimestamptzTwoArg##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
#if defined(__GNUC__) && (__GNUC__ >= 15)
#pragma GCC diagnostic pop
#endif