#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int ILLEGAL_COLUMN; extern const int BAD_ARGUMENTS; } namespace { template struct ActionValueTypeMap {}; template <> struct ActionValueTypeMap { using ActionValueType = UInt16; }; template <> struct ActionValueTypeMap { using ActionValueType = UInt32; }; template <> struct ActionValueTypeMap { using ActionValueType = Int64; }; class FunctionDateNameImpl : public IFunction { public: static constexpr auto name = "dateName"; static FunctionPtr create(ContextPtr) { return std::make_shared(); } String getName() const override { return name; } bool useDefaultImplementationForConstants() const override { return true; } ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {0}; } bool isVariadic() const override { return true; } size_t getNumberOfArguments() const override { return 0; } DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override { if (arguments.size() != 2 && arguments.size() != 3) throw Exception( "Number of arguments for function " + getName() + " doesn't match: passed " + toString(arguments.size()) + ", should be 2 or 3", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); if (!WhichDataType(arguments[0].type).isString()) throw Exception( "Illegal type " + arguments[0].type->getName() + " of 1 argument of function " + getName() + ". Must be string", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); if (!WhichDataType(arguments[1].type).isDateOrDateTime()) throw Exception( "Illegal type " + arguments[1].type->getName() + " of 2 argument of function " + getName() + "Must be a date or a date with time", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); if (arguments.size() == 3 && !WhichDataType(arguments[2].type).isString()) throw Exception( "Illegal type " + arguments[2].type->getName() + " of 3 argument of function " + getName() + "Must be string", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); return std::make_shared(); } ColumnPtr executeImpl( const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, [[maybe_unused]] size_t input_rows_count) const override { ColumnPtr res; if (!((res = executeType(arguments, result_type)) || (res = executeType(arguments, result_type)) || (res = executeType(arguments, result_type)))) throw Exception( "Illegal column " + arguments[1].column->getName() + " of function " + getName() + ", must be Date or DateTime.", ErrorCodes::ILLEGAL_COLUMN); return res; } template ColumnPtr executeType(const ColumnsWithTypeAndName & arguments, const DataTypePtr &) const { auto * times = checkAndGetColumn(arguments[1].column.get()); if (!times) return nullptr; const ColumnConst * datepart_column = checkAndGetColumnConst(arguments[0].column.get()); if (!datepart_column) throw Exception("Illegal column " + arguments[0].column->getName() + " of first ('datepart') argument of function " + getName() + ". Must be constant string.", ErrorCodes::ILLEGAL_COLUMN); using T = typename ActionValueTypeMap::ActionValueType; auto datepart_writer = DatePartWriter(); String datepart = datepart_column->getValue(); if (!datepart_writer.isCorrectDatePart(datepart)) throw Exception("Illegal value " + datepart + " of first ('format') argument of function " + getName() + ". Check documentation.", ErrorCodes::BAD_ARGUMENTS); const DateLUTImpl * time_zone_tmp; if (std::is_same_v || std::is_same_v) time_zone_tmp = &extractTimeZoneFromFunctionArguments(arguments, 2, 1); else time_zone_tmp = &DateLUT::instance(); const auto & vec = times->getData(); const DateLUTImpl & time_zone = *time_zone_tmp; UInt32 scale [[maybe_unused]] = 0; if constexpr (std::is_same_v) { scale = vec.getScale(); } auto col_res = ColumnString::create(); auto & dst_data = col_res->getChars(); auto & dst_offsets = col_res->getOffsets(); dst_data.resize(vec.size() * (9 /* longest possible word 'Wednesday' */ + 1 /* zero terminator */)); dst_offsets.resize(vec.size()); auto * begin = reinterpret_cast(dst_data.data()); auto * pos = begin; for (size_t i = 0; i < vec.size(); ++i) { if constexpr (std::is_same_v) { // since right now LUT does not support Int64-values and not format instructions for subsecond parts, // treat DatTime64 values just as DateTime values by ignoring fractional and casting to UInt32. const auto c = DecimalUtils::split(vec[i], scale); datepart_writer.writeDatePart(pos, datepart, static_cast(c.whole), time_zone); } else { datepart_writer.writeDatePart(pos, datepart, vec[i], time_zone); } dst_offsets[i] = pos - begin; } dst_data.resize(pos - begin); return col_res; } private: template class DatePartWriter { public: void writeDatePart(char *& target, const String & datepart, Time source, const DateLUTImpl & timezone) { datepart_functions.at(datepart)(target, source, timezone); } bool isCorrectDatePart(const String &datepart) { return datepart_functions.find(datepart) != datepart_functions.end(); } private: const std::unordered_map datepart_functions = { {"year", writeYear}, {"quarter", writeQuarter}, {"month", writeMonth}, {"dayofyear", writeDayOfYear}, {"day", writeDay}, {"week", writeWeek}, {"weekday", writeWeekday}, {"hour", writeHour}, {"minute", writeMinute}, {"second", writeSecond}, }; static inline void writeYear(char *& target, Time source, const DateLUTImpl & timezone) { writeNumber(target, ToYearImpl::execute(source, timezone)); } static inline void writeQuarter(char *& target, Time source, const DateLUTImpl & timezone) { writeNumber(target, ToQuarterImpl::execute(source, timezone)); } static inline void writeMonth(char *& target, Time source, const DateLUTImpl & timezone) { const auto month = ToMonthImpl::execute(source, timezone); const String monthnames[12] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; writeString(target, monthnames[month - 1]); } static inline void writeDayOfYear(char *& target, Time source, const DateLUTImpl & timezone) { writeNumber(target, ToDayOfYearImpl::execute(source, timezone)); } static inline void writeDay(char *& target, Time source, const DateLUTImpl & timezone) { writeNumber(target, ToDayOfMonthImpl::execute(source, timezone)); } static inline void writeWeek(char *& target, Time source, const DateLUTImpl & timezone) { writeNumber(target, ToISOWeekImpl::execute(source, timezone)); } static inline void writeWeekday(char *& target, Time source, const DateLUTImpl & timezone) { const auto day = ToDayOfWeekImpl::execute(source, timezone); const String daynames[12] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; writeString(target, daynames[day - 1]); } static inline void writeHour(char *& target, Time source, const DateLUTImpl & timezone) { writeNumber(target, ToHourImpl::execute(source, timezone)); } static inline void writeMinute(char *& target, Time source, const DateLUTImpl & timezone) { writeNumber(target, ToMinuteImpl::execute(source, timezone)); } static inline void writeSecond(char *& target, Time source, const DateLUTImpl & timezone) { writeNumber(target, ToSecondImpl::execute(source, timezone)); } static inline void writeString(char *& target, const String & value) { size_t size = value.size() + 1; /// With zero terminator memcpy(target, value.data(), size); target += size; } template static inline void writeNumber(char *& target, T value) { if (value < 10) { *target = value + '0'; target += 2; *target++ = 0; } else if (value < 100) { writeNumber2(target, value); target += 3; *target++ = 0; } else if (value < 1000) { writeNumber3(target, value); target += 4; *target++ = 0; } else if (value < 10000) { writeNumber4(target, value); target += 5; *target++ = 0; } else { throw Exception("Illegal value of second ('datetime') argument of function datePart. Check documentation.", ErrorCodes::BAD_ARGUMENTS); } } template static inline void writeNumber2(char * p, T v) { memcpy(p, &digits100[v * 2], 2); } template static inline void writeNumber3(char * p, T v) { writeNumber2(p, v / 10); p[2] += v % 10; } template static inline void writeNumber4(char * p, T v) { writeNumber2(p, v / 100); writeNumber2(p + 2, v % 100); } }; }; } void registerFunctionDateName(FunctionFactory & factory) { factory.registerFunction(FunctionFactory::CaseInsensitive); } }