blob: e42560ebb591937697aff59e62f1af8fc99a7d6b [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.
// This file is copied from
// https://github.com/ClickHouse/ClickHouse/blob/master/src/Functions/DateTimeTransforms.h
// and modified by Doris
#pragma once
#include <libdivide.h>
#include <cmath>
#include <cstdint>
#include "common/status.h"
#include "core/binary_cast.hpp"
#include "core/block/block.h"
#include "core/block/column_numbers.h"
#include "core/column/column.h"
#include "core/column/column_decimal.h"
#include "core/column/column_nullable.h"
#include "core/column/column_string.h"
#include "core/column/column_vector.h"
#include "core/data_type/data_type_date.h"
#include "core/data_type/data_type_date_time.h"
#include "core/data_type/data_type_decimal.h"
#include "core/data_type/data_type_string.h"
#include "core/data_type/define_primitive_type.h"
#include "core/data_type/primitive_type.h"
#include "core/types.h"
#include "core/value/time_value.h"
#include "core/value/vdatetime_value.h"
#include "exec/common/int_exp.h"
#include "exec/common/util.hpp"
#include "exprs/function/date_format_type.h"
#include "exprs/function_context.h"
// 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 {
#include "common/compile_check_begin.h"
#define TIME_FUNCTION_IMPL(CLASS, UNIT, FUNCTION) \
template <PrimitiveType PType> \
struct CLASS { \
static constexpr PrimitiveType OpArgType = PType; \
using CppType = typename PrimitiveTypeTraits<PType>::CppType; \
static constexpr auto name = #UNIT; \
\
static inline auto execute(const CppType& date_time_value) { \
return date_time_value.FUNCTION; \
} \
\
static DataTypes get_variadic_argument_types() { \
return {std::make_shared<typename PrimitiveTypeTraits<PType>::DataType>()}; \
} \
}
#define TO_TIME_FUNCTION(CLASS, UNIT) TIME_FUNCTION_IMPL(CLASS, UNIT, UNIT())
TO_TIME_FUNCTION(ToYearImpl, year);
template <PrimitiveType PType>
struct ToCenturyImpl {
static constexpr PrimitiveType OpArgType = PType;
using CppType = typename PrimitiveTypeTraits<PType>::CppType;
static constexpr auto name = "century";
static inline auto execute(const CppType& t) {
const auto& date_time_value = t;
int year = date_time_value.year();
return (year - 1) / 100 + 1;
}
static DataTypes get_variadic_argument_types() {
return {std::make_shared<typename PrimitiveTypeTraits<PType>::DataType>()};
}
};
TO_TIME_FUNCTION(ToYearOfWeekImpl, year_of_week);
TO_TIME_FUNCTION(ToQuarterImpl, quarter);
TO_TIME_FUNCTION(ToMonthImpl, month);
TO_TIME_FUNCTION(ToDayImpl, day);
TO_TIME_FUNCTION(ToHourImpl, hour);
TO_TIME_FUNCTION(ToMinuteImpl, minute);
TO_TIME_FUNCTION(ToSecondImpl, second);
TO_TIME_FUNCTION(ToMicroSecondImpl, microsecond);
TIME_FUNCTION_IMPL(WeekOfYearImpl, weekofyear, week(mysql_week_mode(3)));
TIME_FUNCTION_IMPL(DayOfYearImpl, dayofyear, day_of_year());
TIME_FUNCTION_IMPL(DayOfMonthImpl, dayofmonth, day());
TIME_FUNCTION_IMPL(DayOfWeekImpl, dayofweek, day_of_week());
TIME_FUNCTION_IMPL(WeekDayImpl, weekday, weekday());
// TODO: the method should be always not nullable
TIME_FUNCTION_IMPL(ToDaysImpl, to_days, daynr());
TIME_FUNCTION_IMPL(ToSecondsImpl, to_seconds,
daynr() * 86400L + date_time_value.time_part_to_seconds());
#define TIME_FUNCTION_ONE_ARG_IMPL(CLASS, UNIT, FUNCTION) \
template <PrimitiveType PType> \
struct CLASS { \
static constexpr PrimitiveType OpArgType = PType; \
using ArgType = typename PrimitiveTypeTraits<PType>::CppType; \
static constexpr auto name = #UNIT; \
\
static inline auto execute(const ArgType& t) { \
const auto& date_time_value = (typename PrimitiveTypeTraits<PType>::CppType&)(t); \
return date_time_value.FUNCTION; \
} \
\
static DataTypes get_variadic_argument_types() { \
return {std::make_shared<typename PrimitiveTypeTraits<PType>::DataType>()}; \
} \
}
TIME_FUNCTION_ONE_ARG_IMPL(ToWeekOneArgImpl, week, week(mysql_week_mode(0)));
TIME_FUNCTION_ONE_ARG_IMPL(ToYearWeekOneArgImpl, yearweek, year_week(mysql_week_mode(0)));
template <PrimitiveType PType>
struct ToDateImpl {
static constexpr PrimitiveType OpArgType = PType;
using DateType = typename PrimitiveTypeTraits<PType>::CppType;
static constexpr auto name = "to_date";
static auto execute(const DateType& t) {
if constexpr (std::is_same_v<DateType, DateV2Value<DateV2ValueType>>) {
return t;
} else if constexpr (std::is_same_v<DateType, VecDateTimeValue>) {
t.cast_to_date();
return t;
} else {
return binary_cast<UInt32, DateV2Value<DateV2ValueType>>(
(UInt32)(t.to_date_int_val() >> TIME_PART_LENGTH));
}
}
static DataTypes get_variadic_argument_types() {
return {std::make_shared<typename PrimitiveTypeTraits<PType>::DataType>()};
}
};
template <PrimitiveType ArgType>
struct DateImpl : public ToDateImpl<ArgType> {
static constexpr auto name = "date";
};
// TODO: This function look like no need do indeed copy here, we should optimize this function
template <PrimitiveType PType>
struct TimeStampImpl {
static constexpr PrimitiveType OpArgType = PType;
using ArgType = typename PrimitiveTypeTraits<PType>::CppType;
static constexpr auto name = "timestamp";
static auto execute(const ArgType& t) { return t; }
static DataTypes get_variadic_argument_types() {
return {std::make_shared<typename PrimitiveTypeTraits<PType>::DataType>()};
}
};
template <PrimitiveType PType>
struct DayNameImpl {
static constexpr PrimitiveType OpArgType = PType;
using ArgType = typename PrimitiveTypeTraits<PType>::CppType;
static constexpr auto name = "dayname";
static constexpr auto max_size = MAX_DAY_NAME_LEN;
static auto execute(const typename PrimitiveTypeTraits<PType>::CppType& dt,
ColumnString::Chars& res_data, size_t& offset, const char* const* day_names,
FunctionContext* /*context*/) {
DCHECK(day_names != nullptr);
const auto* day_name = dt.day_name_with_locale(day_names);
if (day_name != nullptr) {
auto len = strlen(day_name);
memcpy(&res_data[offset], day_name, len);
offset += len;
}
return offset;
}
static DataTypes get_variadic_argument_types() {
return {std::make_shared<typename PrimitiveTypeTraits<PType>::DataType>()};
}
};
template <PrimitiveType PType>
struct ToIso8601Impl {
static constexpr PrimitiveType OpArgType = PType;
using ArgType = typename PrimitiveTypeTraits<PType>::CppType;
static constexpr auto name = "to_iso8601";
static constexpr auto max_size = PType == TYPE_DATEV2 ? 10 : 26;
static auto execute(const typename PrimitiveTypeTraits<PType>::CppType& dt,
ColumnString::Chars& res_data, size_t& offset,
const char* const* /*names_ptr*/, FunctionContext* /*context*/) {
auto length = dt.to_buffer((char*)res_data.data() + offset,
std::is_same_v<ArgType, UInt32> ? -1 : 6);
if (PType == TYPE_DATETIMEV2 || PType == TYPE_TIMESTAMPTZ) {
res_data[offset + 10] = 'T';
}
offset += length;
return offset;
}
static DataTypes get_variadic_argument_types() {
return {std::make_shared<typename PrimitiveTypeTraits<PType>::DataType>()};
}
};
// Specialization for TIMESTAMPTZ type
template <>
struct ToIso8601Impl<TYPE_TIMESTAMPTZ> {
static constexpr PrimitiveType OpArgType = TYPE_TIMESTAMPTZ;
using ArgType = typename PrimitiveTypeTraits<TYPE_TIMESTAMPTZ>::CppType;
static constexpr auto name = "to_iso8601";
// Format: YYYY-MM-DDTHH:MM:SS.SSSSSS+HH:MM
static constexpr auto max_size = 32;
static auto execute(const TimestampTzValue& tz_value, ColumnString::Chars& res_data,
size_t& offset, const char* const* /*names_ptr*/,
FunctionContext* context) {
// Get timezone
const auto& local_time_zone = context->state()->timezone_obj();
// Convert UTC time to local time
cctz::civil_second utc_sec(tz_value.year(), tz_value.month(), tz_value.day(),
tz_value.hour(), tz_value.minute(), tz_value.second());
cctz::time_point<cctz::seconds> local_time = cctz::convert(utc_sec, cctz::utc_time_zone());
auto lookup_result = local_time_zone.lookup(local_time);
cctz::civil_second civ = lookup_result.cs;
auto time_offset = lookup_result.offset;
int offset_hours = time_offset / 3600;
int offset_mins = (std::abs(time_offset) % 3600) / 60;
// Create local datetime value
DateV2Value<DateTimeV2ValueType> local_dt;
local_dt.unchecked_set_time((uint16_t)civ.year(), (uint8_t)civ.month(), (uint8_t)civ.day(),
(uint8_t)civ.hour(), (uint8_t)civ.minute(),
(uint8_t)civ.second(), tz_value.microsecond());
// YYYY-MM-DDTHH:MM:SS.SSSSSS+HH:MM
auto length = local_dt.to_buffer((char*)res_data.data() + offset, 6);
res_data[offset + 10] = 'T';
res_data[offset + length] = (offset_hours >= 0 ? '+' : '-');
res_data[offset + length + 1] = static_cast<char>('0' + std::abs(offset_hours) / 10);
res_data[offset + length + 2] = '0' + std::abs(offset_hours) % 10;
res_data[offset + length + 3] = ':';
res_data[offset + length + 4] = static_cast<char>('0' + offset_mins / 10);
res_data[offset + length + 5] = '0' + offset_mins % 10;
offset += length + 6;
return offset;
}
static DataTypes get_variadic_argument_types() {
return {std::make_shared<typename PrimitiveTypeTraits<TYPE_TIMESTAMPTZ>::DataType>()};
}
};
template <PrimitiveType PType>
struct MonthNameImpl {
static constexpr PrimitiveType OpArgType = PType;
using ArgType = typename PrimitiveTypeTraits<PType>::CppType;
static constexpr auto name = "monthname";
static constexpr auto max_size = MAX_MONTH_NAME_LEN;
static auto execute(const typename PrimitiveTypeTraits<PType>::CppType& dt,
ColumnString::Chars& res_data, size_t& offset,
const char* const* month_names, FunctionContext* /*context*/) {
DCHECK(month_names != nullptr);
const auto* month_name = dt.month_name_with_locale(month_names);
if (month_name != nullptr) {
auto len = strlen(month_name);
memcpy(&res_data[offset], month_name, len);
offset += len;
}
return offset;
}
static DataTypes get_variadic_argument_types() {
return {std::make_shared<typename PrimitiveTypeTraits<PType>::DataType>()};
}
};
template <typename FormatImpl, const char* FuncName>
struct DateTimeV2FormatImpl {
static constexpr PrimitiveType OpArgType = TYPE_DATETIMEV2;
using ArgType = typename PrimitiveTypeTraits<TYPE_DATETIMEV2>::CppType;
static constexpr auto name = FuncName;
static constexpr auto max_size = FormatImpl::row_size;
static auto execute(const ArgType& dt, ColumnString::Chars& res_data, size_t& offset,
const char* const* /*names_ptr*/, FunctionContext* /*context*/) {
auto* buf = reinterpret_cast<char*>(&res_data[offset]);
offset += FormatImpl::date_to_str(dt, buf);
return offset;
}
static DataTypes get_variadic_argument_types() {
return {std::make_shared<typename PrimitiveTypeTraits<TYPE_DATETIMEV2>::DataType>()};
}
};
inline constexpr char kYearMonthName[] = "year_month";
inline constexpr char kDayHourName[] = "day_hour";
inline constexpr char kDayMinuteName[] = "day_minute";
inline constexpr char kDaySecondName[] = "day_second";
inline constexpr char kDayMicrosecondName[] = "day_microsecond";
inline constexpr char kHourMinuteName[] = "hour_minute";
inline constexpr char kHourSecondName[] = "hour_second";
inline constexpr char kHourMicrosecondName[] = "hour_microsecond";
inline constexpr char kMinuteSecondName[] = "minute_second";
inline constexpr char kMinuteMicrosecondName[] = "minute_microsecond";
inline constexpr char kSecondMicrosecondName[] = "second_microsecond";
using YearMonthImpl = DateTimeV2FormatImpl<time_format_type::yyyy_MMImpl, kYearMonthName>;
using DayHourImpl = DateTimeV2FormatImpl<time_format_type::dd_HHImpl, kDayHourName>;
using DayMinuteImpl = DateTimeV2FormatImpl<time_format_type::dd_HH_mmImpl, kDayMinuteName>;
using DaySecondImpl = DateTimeV2FormatImpl<time_format_type::dd_HH_mm_ssImpl, kDaySecondName>;
using DayMicrosecondImpl =
DateTimeV2FormatImpl<time_format_type::dd_HH_mm_ss_SSSSSSImpl, kDayMicrosecondName>;
using HourMinuteImpl = DateTimeV2FormatImpl<time_format_type::HH_mmImpl, kHourMinuteName>;
using HourSecondImpl = DateTimeV2FormatImpl<time_format_type::HH_mm_ssImpl, kHourSecondName>;
using HourMicrosecondImpl =
DateTimeV2FormatImpl<time_format_type::HH_mm_ss_SSSSSSImpl, kHourMicrosecondName>;
using MinuteSecondImpl = DateTimeV2FormatImpl<time_format_type::mm_ssImpl, kMinuteSecondName>;
using MinuteMicrosecondImpl =
DateTimeV2FormatImpl<time_format_type::mm_ss_SSSSSSImpl, kMinuteMicrosecondName>;
using SecondMicrosecondImpl =
DateTimeV2FormatImpl<time_format_type::ss_SSSSSSImpl, kSecondMicrosecondName>;
template <PrimitiveType PType>
struct DateFormatImpl {
using DateType = typename PrimitiveTypeTraits<PType>::CppType;
using ArgType = typename PrimitiveTypeTraits<PType>::CppType;
static constexpr PrimitiveType FromPType = PType;
static constexpr auto name = "date_format";
template <typename Impl>
static bool execute(const DateType& dt, StringRef format, ColumnString::Chars& res_data,
size_t& offset, const cctz::time_zone& time_zone) {
if constexpr (std::is_same_v<Impl, time_format_type::UserDefinedImpl>) {
// Handle non-special formats.
char buf[100 + SAFE_FORMAT_STRING_MARGIN];
if (!dt.to_format_string_conservative(format.data, format.size, buf,
100 + SAFE_FORMAT_STRING_MARGIN)) {
return true;
}
auto len = strlen(buf);
res_data.insert(buf, buf + len);
offset += len;
return false;
} else {
if (!dt.is_valid_date()) {
return true;
}
// No buffer is needed here because these specially optimized formats have fixed lengths,
// and sufficient memory has already been reserved.
auto len = Impl::date_to_str(dt, (char*)res_data.data() + offset);
offset += len;
return false;
}
}
static DataTypes get_variadic_argument_types() {
return {std::make_shared<typename PrimitiveTypeTraits<PType>::DataType>(),
std::make_shared<DataTypeString>()};
}
};
template <bool WithStringArg, bool NewVersion = false>
struct FromUnixTimeImpl {
using ArgType = Int64;
static constexpr PrimitiveType FromPType = TYPE_BIGINT;
static DataTypes get_variadic_argument_types() {
if constexpr (WithStringArg) {
return {std::make_shared<DataTypeInt64>(), std::make_shared<DataTypeString>()};
} else {
return {std::make_shared<DataTypeInt64>()};
}
}
static const int64_t TIMESTAMP_VALID_MAX = 32536771199;
static constexpr auto name = NewVersion ? "from_unixtime_new" : "from_unixtime";
[[nodiscard]] static bool check_valid(const ArgType& val) {
if constexpr (NewVersion) {
if (val < 0) [[unlikely]] {
return false;
}
} else {
if (val < 0 || val > TIMESTAMP_VALID_MAX) [[unlikely]] {
return false;
}
}
return true;
}
static DateV2Value<DateTimeV2ValueType> get_datetime_value(const ArgType& val,
const cctz::time_zone& time_zone) {
DateV2Value<DateTimeV2ValueType> dt;
dt.from_unixtime(val, time_zone);
return dt;
}
// return true if null(result is invalid)
template <typename Impl>
static bool execute(const ArgType& val, StringRef format, ColumnString::Chars& res_data,
size_t& offset, const cctz::time_zone& time_zone) {
if (!check_valid(val)) {
return true;
}
DateV2Value<DateTimeV2ValueType> dt = get_datetime_value(val, time_zone);
if (!dt.is_valid_date()) [[unlikely]] {
return true;
}
if constexpr (std::is_same_v<Impl, time_format_type::UserDefinedImpl>) {
char buf[100 + SAFE_FORMAT_STRING_MARGIN];
if (!dt.to_format_string_conservative(format.data, format.size, buf,
100 + SAFE_FORMAT_STRING_MARGIN)) {
return true;
}
auto len = strlen(buf);
res_data.insert(buf, buf + len);
offset += len;
} else {
// No buffer is needed here because these specially optimized formats have fixed lengths,
// and sufficient memory has already been reserved.
auto len = Impl::date_to_str(dt, (char*)res_data.data() + offset);
offset += len;
}
return false;
}
};
// only new verison
template <bool WithStringArg>
struct FromUnixTimeDecimalImpl {
using ArgType = Int64;
static constexpr PrimitiveType FromPType = TYPE_DECIMAL64;
constexpr static short Scale = 6; // same with argument's scale in FE's signature
static DataTypes get_variadic_argument_types() {
if constexpr (WithStringArg) {
return {std::make_shared<DataTypeDecimal64>(), std::make_shared<DataTypeString>()};
} else {
return {std::make_shared<DataTypeDecimal64>()};
}
}
static constexpr auto name = "from_unixtime_new";
[[nodiscard]] static bool check_valid(const ArgType& val) {
if (val < 0) [[unlikely]] {
return false;
}
return true;
}
static DateV2Value<DateTimeV2ValueType> get_datetime_value(const ArgType& interger,
const ArgType& fraction,
const cctz::time_zone& time_zone) {
DateV2Value<DateTimeV2ValueType> dt;
// 9 is nanoseconds, our input's scale is 6
dt.from_unixtime(interger, (int32_t)fraction * common::exp10_i32(9 - Scale), time_zone, 6);
return dt;
}
// return true if null(result is invalid)
template <typename Impl>
static bool execute_decimal(const ArgType& interger, const ArgType& fraction, StringRef format,
ColumnString::Chars& res_data, size_t& offset,
const cctz::time_zone& time_zone) {
if (!check_valid(interger + (fraction > 0 ? 1 : ((fraction < 0) ? -1 : 0)))) [[unlikely]] {
return true;
}
DateV2Value<DateTimeV2ValueType> dt = get_datetime_value(interger, fraction, time_zone);
if (!dt.is_valid_date()) [[unlikely]] {
return true;
}
if constexpr (std::is_same_v<Impl, time_format_type::UserDefinedImpl>) {
char buf[100 + SAFE_FORMAT_STRING_MARGIN];
if (!dt.to_format_string_conservative(format.data, format.size, buf,
100 + SAFE_FORMAT_STRING_MARGIN)) {
return true;
}
auto len = strlen(buf);
res_data.insert(buf, buf + len);
offset += len;
} else {
// No buffer is needed here because these specially optimized formats have fixed lengths,
// and sufficient memory has already been reserved.
auto len = time_format_type::yyyy_MM_dd_HH_mm_ss_SSSSSSImpl::date_to_str(
dt, (char*)res_data.data() + offset);
offset += len;
}
return false;
}
};
// Base template for optimized time field(HOUR, MINUTE, SECOND, MS) extraction from Unix timestamp
// Uses lookup_offset to avoid expensive civil_second construction
template <typename Impl>
class FunctionTimeFieldFromUnixtime : public IFunction {
public:
static constexpr auto name = Impl::name;
static FunctionPtr create() { return std::make_shared<FunctionTimeFieldFromUnixtime<Impl>>(); }
String get_name() const override { return name; }
size_t get_number_of_arguments() const override { return 1; }
DataTypePtr get_return_type_impl(const ColumnsWithTypeAndName& arguments) const override {
// microsecond_from_unixtime returns Int32, others (hour/minute/second) return Int8
if constexpr (Impl::ArgType == PrimitiveType::TYPE_DECIMAL64) {
return make_nullable(std::make_shared<DataTypeInt32>());
} else {
return make_nullable(std::make_shared<DataTypeInt8>());
}
}
// (UTC 9999-12-31 23:59:59) - 24 * 3600
static const int64_t TIMESTAMP_VALID_MAX = 253402243199L;
Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
uint32_t result, size_t input_rows_count) const override {
using ArgColType = typename PrimitiveTypeTraits<Impl::ArgType>::ColumnType;
using ResColType = std::conditional_t<Impl::ArgType == PrimitiveType::TYPE_DECIMAL64,
ColumnInt32, ColumnInt8>;
using ResItemType = typename ResColType::value_type;
auto res = ResColType::create();
const auto* ts_col =
assert_cast<const ArgColType*>(block.get_by_position(arguments[0]).column.get());
if constexpr (Impl::ArgType == PrimitiveType::TYPE_DECIMAL64) {
// microsecond_from_unixtime only
const auto scale = static_cast<int32_t>(ts_col->get_scale());
for (int i = 0; i < input_rows_count; ++i) {
const auto seconds = ts_col->get_intergral_part(i);
const auto fraction = ts_col->get_fractional_part(i);
if (seconds < 0 || seconds > TIMESTAMP_VALID_MAX) {
return Status::InvalidArgument(
"The input value of TimeFiled(from_unixtime()) must between 0 and "
"253402243199L");
}
ResItemType value = Impl::extract_field(fraction, scale);
res->insert_value(value);
}
} else {
auto ctz = context->state()->timezone_obj();
for (int i = 0; i < input_rows_count; ++i) {
auto date = ts_col->get_element(i);
if (date < 0 || date > TIMESTAMP_VALID_MAX) {
return Status::InvalidArgument(
"The input value of TimeFiled(from_unixtime()) must between 0 and "
"253402243199L");
}
ResItemType value = Impl::extract_field(date, ctz);
res->insert_value(value);
}
}
block.replace_by_position(result, std::move(res));
return Status::OK();
}
};
struct HourFromUnixtimeImpl {
static constexpr PrimitiveType ArgType = PrimitiveType::TYPE_BIGINT;
static constexpr auto name = "hour_from_unixtime";
static int8_t extract_field(int64_t local_time, const cctz::time_zone& ctz) {
static const auto epoch = std::chrono::time_point_cast<cctz::sys_seconds>(
std::chrono::system_clock::from_time_t(0));
cctz::time_point<cctz::sys_seconds> t = epoch + cctz::seconds(local_time);
int offset = ctz.lookup_offset(t).offset;
local_time += offset;
static const libdivide::divider<int64_t> fast_div_3600(3600);
static const libdivide::divider<int64_t> fast_div_86400(86400);
int64_t remainder;
if (LIKELY(local_time >= 0)) {
remainder = local_time - local_time / fast_div_86400 * 86400;
} else {
remainder = local_time % 86400;
if (remainder < 0) {
remainder += 86400;
}
}
return static_cast<int8_t>(remainder / fast_div_3600);
}
};
struct MinuteFromUnixtimeImpl {
static constexpr PrimitiveType ArgType = PrimitiveType::TYPE_BIGINT;
static constexpr auto name = "minute_from_unixtime";
static int8_t extract_field(int64_t local_time, const cctz::time_zone& /*ctz*/) {
static const libdivide::divider<int64_t> fast_div_60(60);
static const libdivide::divider<int64_t> fast_div_3600(3600);
local_time = local_time - local_time / fast_div_3600 * 3600;
return static_cast<int8_t>(local_time / fast_div_60);
}
};
struct SecondFromUnixtimeImpl {
static constexpr PrimitiveType ArgType = PrimitiveType::TYPE_BIGINT;
static constexpr auto name = "second_from_unixtime";
static int8_t extract_field(int64_t local_time, const cctz::time_zone& /*ctz*/) {
return static_cast<int8_t>(local_time % 60);
}
};
struct MicrosecondFromUnixtimeImpl {
static constexpr PrimitiveType ArgType = PrimitiveType::TYPE_DECIMAL64;
static constexpr auto name = "microsecond_from_unixtime";
static int32_t extract_field(int64_t fraction, int scale) {
if (scale < 6) {
fraction *= common::exp10_i64(6 - scale);
}
return static_cast<int32_t>(fraction);
}
};
template <PrimitiveType ArgPType>
class FunctionTimeFormat : public IFunction {
public:
using ArgColType = typename PrimitiveTypeTraits<ArgPType>::ColumnType;
using ArgCppType = typename PrimitiveTypeTraits<ArgPType>::CppType;
static constexpr auto name = "time_format";
String get_name() const override { return name; }
static FunctionPtr create() { return std::make_shared<FunctionTimeFormat>(); }
DataTypes get_variadic_argument_types_impl() const override {
return {std::make_shared<typename PrimitiveTypeTraits<ArgPType>::DataType>(),
std::make_shared<DataTypeString>()};
}
DataTypePtr get_return_type_impl(const ColumnsWithTypeAndName& arguments) const override {
return make_nullable(std::make_shared<DataTypeString>());
}
size_t get_number_of_arguments() const override { return 2; }
Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
uint32_t result, size_t input_rows_count) const override {
auto res_col = ColumnString::create();
ColumnString::Chars& res_chars = res_col->get_chars();
ColumnString::Offsets& res_offsets = res_col->get_offsets();
auto null_map = ColumnUInt8::create();
auto& null_map_data = null_map->get_data();
null_map_data.resize_fill(input_rows_count, 0);
res_offsets.reserve(input_rows_count);
ColumnPtr arg_col[2];
bool is_const[2];
for (size_t i = 0; i < 2; ++i) {
const ColumnPtr& col = block.get_by_position(arguments[i]).column;
std::tie(arg_col[i], is_const[i]) = unpack_if_const(col);
}
const auto* datetime_col = assert_cast<const ArgColType*>(arg_col[0].get());
const auto* format_col = assert_cast<const ColumnString*>(arg_col[1].get());
for (size_t i = 0; i < input_rows_count; ++i) {
const auto& datetime_val = datetime_col->get_element(index_check_const(i, is_const[0]));
StringRef format = format_col->get_data_at(index_check_const(i, is_const[1]));
TimeValue::TimeType time = get_time_value(datetime_val);
char buf[100 + SAFE_FORMAT_STRING_MARGIN];
if (!TimeValue::to_format_string_conservative(format.data, format.size, buf,
100 + SAFE_FORMAT_STRING_MARGIN, time)) {
null_map_data[i] = 1;
res_offsets.push_back(res_chars.size());
continue;
}
res_chars.insert(buf, buf + strlen(buf));
res_offsets.push_back(res_chars.size());
}
block.replace_by_position(result,
ColumnNullable::create(std::move(res_col), std::move(null_map)));
return Status::OK();
}
private:
TimeValue::TimeType get_time_value(const ArgCppType& datetime_val) const {
if constexpr (ArgPType == PrimitiveType::TYPE_TIMEV2) {
return static_cast<TimeValue::TimeType>(datetime_val);
} else {
return TimeValue::make_time(datetime_val.hour(), datetime_val.minute(),
datetime_val.second(), datetime_val.microsecond());
}
}
};
#include "common/compile_check_end.h"
} // namespace doris
#if defined(__GNUC__) && (__GNUC__ >= 15)
#pragma GCC diagnostic pop
#endif