From 0288cc5a1bf1c6ffb8d41183fd5877df9b132f1a Mon Sep 17 00:00:00 2001 From: dankondr Date: Wed, 14 Apr 2021 23:29:17 +0300 Subject: [PATCH 01/21] Add dateName function --- src/Functions/dateName.cpp | 324 +++++++++++++++++++ src/Functions/registerFunctionsDateTime.cpp | 2 + src/Functions/ya.make | 1 + tests/queries/0_stateless/01811_datename.sql | 59 ++++ 4 files changed, 386 insertions(+) create mode 100644 src/Functions/dateName.cpp create mode 100644 tests/queries/0_stateless/01811_datename.sql diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp new file mode 100644 index 00000000000..7c25159775e --- /dev/null +++ b/src/Functions/dateName.cpp @@ -0,0 +1,324 @@ +#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_ARGUMENT; +} + +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; } + + 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[1].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_ARGUMENT); + + 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_ARGUMENT); + } + } + + 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); +} +} diff --git a/src/Functions/registerFunctionsDateTime.cpp b/src/Functions/registerFunctionsDateTime.cpp index 441f28bfb54..abbc52c8360 100644 --- a/src/Functions/registerFunctionsDateTime.cpp +++ b/src/Functions/registerFunctionsDateTime.cpp @@ -64,6 +64,7 @@ void registerFunctionSubtractMonths(FunctionFactory &); void registerFunctionSubtractQuarters(FunctionFactory &); void registerFunctionSubtractYears(FunctionFactory &); void registerFunctionDateDiff(FunctionFactory &); +void registerFunctionDateName(FunctionFactory &); void registerFunctionToTimeZone(FunctionFactory &); void registerFunctionFormatDateTime(FunctionFactory &); void registerFunctionFromModifiedJulianDay(FunctionFactory &); @@ -134,6 +135,7 @@ void registerFunctionsDateTime(FunctionFactory & factory) registerFunctionSubtractQuarters(factory); registerFunctionSubtractYears(factory); registerFunctionDateDiff(factory); + registerFunctionDateName(factory); registerFunctionToTimeZone(factory); registerFunctionFormatDateTime(factory); registerFunctionFromModifiedJulianDay(factory); diff --git a/src/Functions/ya.make b/src/Functions/ya.make index 52ed54ec64f..486629e7ee9 100644 --- a/src/Functions/ya.make +++ b/src/Functions/ya.make @@ -222,6 +222,7 @@ SRCS( currentDatabase.cpp currentUser.cpp dateDiff.cpp + dateName.cpp date_trunc.cpp decodeXMLComponent.cpp decrypt.cpp diff --git a/tests/queries/0_stateless/01811_datename.sql b/tests/queries/0_stateless/01811_datename.sql new file mode 100644 index 00000000000..87ece62f78a --- /dev/null +++ b/tests/queries/0_stateless/01811_datename.sql @@ -0,0 +1,59 @@ +SELECT dateName('year', toDateTime('2021-04-14 11:22:33')); +SELECT dateName('quarter', toDateTime('2021-04-14 11:22:33')); +SELECT dateName('month', toDateTime('2021-04-14 11:22:33')); +SELECT dateName('dayofyear', toDateTime('2021-04-14 11:22:33')); +SELECT dateName('day', toDateTime('2021-04-14 11:22:33')); +SELECT dateName('week', toDateTime('2021-04-14 11:22:33')); +SELECT dateName('weekday', toDateTime('2021-04-14 11:22:33')); +SELECT dateName('hour', toDateTime('2021-04-14 11:22:33')); +SELECT dateName('minute', toDateTime('2021-04-14 11:22:33')); +SELECT dateName('second', toDateTime('2021-04-14 11:22:33')); + + +SELECT dateName('year', toDateTime64('2021-04-14 11:22:33')); +SELECT dateName('quarter', toDateTime64('2021-04-14 11:22:33')); +SELECT dateName('month', toDateTime64('2021-04-14 11:22:33')); +SELECT dateName('dayofyear', toDateTime64('2021-04-14 11:22:33')); +SELECT dateName('day', toDateTime64('2021-04-14 11:22:33')); +SELECT dateName('week', toDateTime64('2021-04-14 11:22:33')); +SELECT dateName('weekday', toDateTime64('2021-04-14 11:22:33')); +SELECT dateName('hour', toDateTime64('2021-04-14 11:22:33')); +SELECT dateName('minute', toDateTime64('2021-04-14 11:22:33')); +SELECT dateName('second', toDateTime64('2021-04-14 11:22:33')); + + +SELECT dateName('year', toDate('2021-04-14')); +SELECT dateName('quarter', toDate('2021-04-14')); +SELECT dateName('month', toDate('2021-04-14')); +SELECT dateName('dayofyear', toDate('2021-04-14')); +SELECT dateName('day', toDate('2021-04-14')); +SELECT dateName('week', toDate('2021-04-14')); +SELECT dateName('weekday', toDate('2021-04-14')); +SELECT dateName('hour', toDate('2021-04-14')); +SELECT dateName('minute', toDate('2021-04-14')); +SELECT dateName('second', toDate('2021-04-14')); + + +SELECT dateName('day', toDateTime('2021-04-14 11:22:33'), 'Europe/Moscow'), + dateName('day', toDateTime('2021-04-14 11:22:33'), 'UTC'); + +SELECT dateName('day', toDate('2021-04-12')); +SELECT dateName('day', toDate('2021-04-13')); +SELECT dateName('day', toDate('2021-04-14')); +SELECT dateName('day', toDate('2021-04-15')); +SELECT dateName('day', toDate('2021-04-16')); +SELECT dateName('day', toDate('2021-04-17')); +SELECT dateName('day', toDate('2021-04-18')); + +SELECT dateName('month', toDate('2021-01-14')); +SELECT dateName('month', toDate('2021-02-14')); +SELECT dateName('month', toDate('2021-03-14')); +SELECT dateName('month', toDate('2021-04-14')); +SELECT dateName('month', toDate('2021-05-14')); +SELECT dateName('month', toDate('2021-06-14')); +SELECT dateName('month', toDate('2021-07-14')); +SELECT dateName('month', toDate('2021-08-14')); +SELECT dateName('month', toDate('2021-09-14')); +SELECT dateName('month', toDate('2021-10-14')); +SELECT dateName('month', toDate('2021-11-14')); +SELECT dateName('month', toDate('2021-12-14')); From e6716779b2ec28d9d3b61e27e0ecf1d8f892bb38 Mon Sep 17 00:00:00 2001 From: dankondr Date: Thu, 15 Apr 2021 20:08:55 +0300 Subject: [PATCH 02/21] Fix BAD_ARGUMENTS error code --- src/Functions/dateName.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index 7c25159775e..725954b6874 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -27,7 +27,7 @@ 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_ARGUMENT; + extern const int BAD_ARGUMENTS; } namespace { @@ -118,7 +118,7 @@ public: throw Exception("Illegal value " + datepart + " of first ('format') argument of function " + getName() + ". Check documentation.", - ErrorCodes::BAD_ARGUMENT); + ErrorCodes::BAD_ARGUMENTS); const DateLUTImpl * time_zone_tmp; if (std::is_same_v || std::is_same_v) @@ -290,7 +290,7 @@ private: else { throw Exception("Illegal value of second ('datetime') argument of function datePart. Check documentation.", - ErrorCodes::BAD_ARGUMENT); + ErrorCodes::BAD_ARGUMENTS); } } From af48735e86d8d5dbbcfbefe25147dabc75a47712 Mon Sep 17 00:00:00 2001 From: dankondr Date: Thu, 15 Apr 2021 20:34:21 +0300 Subject: [PATCH 03/21] Fix argument check --- src/Functions/dateName.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index 725954b6874..4e42e9705c3 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -73,7 +73,7 @@ public: ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); if (arguments.size() == 3 && !WhichDataType(arguments[2].type).isString()) throw Exception( - "Illegal type " + arguments[1].type->getName() + " of 3 argument of function " + getName() + "Illegal type " + arguments[2].type->getName() + " of 3 argument of function " + getName() + "Must be string", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); From 71137fd19b0a9f26b0d44a7f95f1be9473c4d48b Mon Sep 17 00:00:00 2001 From: dankondr Date: Fri, 16 Apr 2021 19:56:39 +0300 Subject: [PATCH 04/21] Override getArgumentsThatAreAlwaysConstant --- src/Functions/dateName.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index 4e42e9705c3..5b9e984df96 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -51,6 +51,8 @@ public: 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; } From bce42dc973dff1284d9771b74c9e34f336ed500f Mon Sep 17 00:00:00 2001 From: dankondr Date: Fri, 16 Apr 2021 20:10:48 +0300 Subject: [PATCH 05/21] Rewrite exception throws to fmt --- src/Functions/dateName.cpp | 54 +++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index 5b9e984df96..8139c897a01 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -60,25 +60,28 @@ public: { 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); + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, + "Number of arguments for function {} doesn't match: passed {}", + getName(), + toString(arguments.size())); 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); + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of 1 argument of function {}. Must be string", + arguments[0].type->getName(), + getName()); 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); + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of 2 argument of function {}. Must be a date or a date with time", + arguments[1].type->getName(), + getName()); 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); - + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, + "Illegal type {} of 3 argument of function {}. Must be string", + arguments[2].type->getName(), + getName()); return std::make_shared(); } @@ -91,9 +94,10 @@ public: || (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); + ErrorCodes::ILLEGAL_COLUMN, + "Illegal column {} of function {], must be Date or DateTime.", + arguments[1].column->getName(), + getName()); return res; } @@ -107,20 +111,22 @@ public: 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); + throw Exception( + ErrorCodes::ILLEGAL_COLUMN, + "Illegal column {} of first ('datepart') argument of function {}. Must be constant string.", + arguments[0].column->getName(), + getName()); 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); + throw Exception( + ErrorCodes::BAD_ARGUMENTS, + "Illegal value {} of first ('format') argument of function {}. Check documentation", + datepart, + getName()); const DateLUTImpl * time_zone_tmp; if (std::is_same_v || std::is_same_v) From a041cc9db0aa2c705a2a11146b006dba08b98c86 Mon Sep 17 00:00:00 2001 From: dankondr Date: Fri, 16 Apr 2021 20:32:37 +0300 Subject: [PATCH 06/21] Fix problem with offsets --- src/Functions/dateName.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index 8139c897a01..28f9361fd97 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -166,6 +166,7 @@ public: datepart_writer.writeDatePart(pos, datepart, vec[i], time_zone); } dst_offsets[i] = pos - begin; + ++pos; } dst_data.resize(pos - begin); return col_res; @@ -275,25 +276,25 @@ private: { *target = value + '0'; target += 2; - *target++ = 0; + *target = 0; } else if (value < 100) { writeNumber2(target, value); target += 3; - *target++ = 0; + *target = 0; } else if (value < 1000) { writeNumber3(target, value); target += 4; - *target++ = 0; + *target = 0; } else if (value < 10000) { writeNumber4(target, value); target += 5; - *target++ = 0; + *target = 0; } else { From 58ed68c28ef6eaa6ccba0bafa4d9dde304fcdaa4 Mon Sep 17 00:00:00 2001 From: dankondr Date: Fri, 16 Apr 2021 20:44:56 +0300 Subject: [PATCH 07/21] Fix writeNumber2 --- src/Functions/dateName.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index 28f9361fd97..3d0211a6bbf 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -313,7 +313,7 @@ private: static inline void writeNumber3(char * p, T v) { writeNumber2(p, v / 10); - p[2] += v % 10; + p[2] = v % 10 + '0'; } template From 4c37eba92b402d5bb522e595479be2836aec9686 Mon Sep 17 00:00:00 2001 From: dankondr Date: Fri, 16 Apr 2021 20:46:46 +0300 Subject: [PATCH 08/21] Improve code readability --- src/Functions/dateName.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index 3d0211a6bbf..da45fcb7dcb 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -276,25 +276,25 @@ private: { *target = value + '0'; target += 2; - *target = 0; + *target = '\0'; } else if (value < 100) { writeNumber2(target, value); target += 3; - *target = 0; + *target = '\0'; } else if (value < 1000) { writeNumber3(target, value); target += 4; - *target = 0; + *target = '\0'; } else if (value < 10000) { writeNumber4(target, value); target += 5; - *target = 0; + *target = '\0'; } else { From be97fd358e36d15baef40cffe0ae6f1abde4432a Mon Sep 17 00:00:00 2001 From: dankondr Date: Fri, 16 Apr 2021 20:55:46 +0300 Subject: [PATCH 09/21] Fix problem with 3rd argument being const --- src/Functions/dateName.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index da45fcb7dcb..1d6f5352a78 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -51,7 +51,7 @@ public: bool useDefaultImplementationForConstants() const override { return true; } - ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {0}; } + ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {0, 2}; } bool isVariadic() const override { return true; } size_t getNumberOfArguments() const override { return 0; } From 107357f96194919490a25c017ea900b5e15bcbd9 Mon Sep 17 00:00:00 2001 From: dankondr Date: Fri, 16 Apr 2021 20:59:10 +0300 Subject: [PATCH 10/21] Fix tests data --- tests/queries/0_stateless/01811_datename.sql | 23 +++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/tests/queries/0_stateless/01811_datename.sql b/tests/queries/0_stateless/01811_datename.sql index 87ece62f78a..042bf0a121a 100644 --- a/tests/queries/0_stateless/01811_datename.sql +++ b/tests/queries/0_stateless/01811_datename.sql @@ -10,16 +10,16 @@ SELECT dateName('minute', toDateTime('2021-04-14 11:22:33')); SELECT dateName('second', toDateTime('2021-04-14 11:22:33')); -SELECT dateName('year', toDateTime64('2021-04-14 11:22:33')); -SELECT dateName('quarter', toDateTime64('2021-04-14 11:22:33')); -SELECT dateName('month', toDateTime64('2021-04-14 11:22:33')); -SELECT dateName('dayofyear', toDateTime64('2021-04-14 11:22:33')); -SELECT dateName('day', toDateTime64('2021-04-14 11:22:33')); -SELECT dateName('week', toDateTime64('2021-04-14 11:22:33')); -SELECT dateName('weekday', toDateTime64('2021-04-14 11:22:33')); -SELECT dateName('hour', toDateTime64('2021-04-14 11:22:33')); -SELECT dateName('minute', toDateTime64('2021-04-14 11:22:33')); -SELECT dateName('second', toDateTime64('2021-04-14 11:22:33')); +SELECT dateName('year', toDateTime64('2021-04-14 11:22:33', 3)); +SELECT dateName('quarter', toDateTime64('2021-04-14 11:22:33', 3)); +SELECT dateName('month', toDateTime64('2021-04-14 11:22:33', 3)); +SELECT dateName('dayofyear', toDateTime64('2021-04-14 11:22:33', 3)); +SELECT dateName('day', toDateTime64('2021-04-14 11:22:33', 3)); +SELECT dateName('week', toDateTime64('2021-04-14 11:22:33', 3)); +SELECT dateName('weekday', toDateTime64('2021-04-14 11:22:33', 3)); +SELECT dateName('hour', toDateTime64('2021-04-14 11:22:33', 3)); +SELECT dateName('minute', toDateTime64('2021-04-14 11:22:33', 3)); +SELECT dateName('second', toDateTime64('2021-04-14 11:22:33', 3)); SELECT dateName('year', toDate('2021-04-14')); @@ -29,9 +29,6 @@ SELECT dateName('dayofyear', toDate('2021-04-14')); SELECT dateName('day', toDate('2021-04-14')); SELECT dateName('week', toDate('2021-04-14')); SELECT dateName('weekday', toDate('2021-04-14')); -SELECT dateName('hour', toDate('2021-04-14')); -SELECT dateName('minute', toDate('2021-04-14')); -SELECT dateName('second', toDate('2021-04-14')); SELECT dateName('day', toDateTime('2021-04-14 11:22:33'), 'Europe/Moscow'), From 7e741338f2f69a472e49c81694f9ccc469e19e66 Mon Sep 17 00:00:00 2001 From: dankondr Date: Fri, 16 Apr 2021 21:24:38 +0300 Subject: [PATCH 11/21] Fix typo --- src/Functions/dateName.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index 1d6f5352a78..b493dc7f7ab 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -298,7 +298,7 @@ private: } else { - throw Exception("Illegal value of second ('datetime') argument of function datePart. Check documentation.", + throw Exception("Illegal value of second ('datetime') argument of function dateName. Check documentation.", ErrorCodes::BAD_ARGUMENTS); } } From 6675ed0681c0d077143e1dd473dd1d451446ef85 Mon Sep 17 00:00:00 2001 From: dankondr Date: Tue, 27 Apr 2021 17:26:00 +0300 Subject: [PATCH 12/21] Add tests --- .../0_stateless/01811_datename.reference | 47 +++++++++++++++++++ tests/queries/0_stateless/01811_datename.sql | 4 +- 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 tests/queries/0_stateless/01811_datename.reference diff --git a/tests/queries/0_stateless/01811_datename.reference b/tests/queries/0_stateless/01811_datename.reference new file mode 100644 index 00000000000..8bfdac794ba --- /dev/null +++ b/tests/queries/0_stateless/01811_datename.reference @@ -0,0 +1,47 @@ +2021 +2 +April +104 +14 +15 +Wednesday +11 +22 +33 +2021 +2 +April +104 +14 +15 +Wednesday +11 +22 +33 +2021 +2 +April +104 +14 +15 +Wednesday +14 11 +Monday +Tuesday +Wednesday +Thursday +Friday +Saturday +Sunday +January +February +March +April +May +June +July +August +September +October +November +December \ No newline at end of file diff --git a/tests/queries/0_stateless/01811_datename.sql b/tests/queries/0_stateless/01811_datename.sql index 042bf0a121a..7ecc7699462 100644 --- a/tests/queries/0_stateless/01811_datename.sql +++ b/tests/queries/0_stateless/01811_datename.sql @@ -31,8 +31,8 @@ SELECT dateName('week', toDate('2021-04-14')); SELECT dateName('weekday', toDate('2021-04-14')); -SELECT dateName('day', toDateTime('2021-04-14 11:22:33'), 'Europe/Moscow'), - dateName('day', toDateTime('2021-04-14 11:22:33'), 'UTC'); +SELECT dateName('hour', toDateTime('2021-04-14 11:22:33'), 'Europe/Moscow'), + dateName('hour', toDateTime('2021-04-14 11:22:33'), 'UTC'); SELECT dateName('day', toDate('2021-04-12')); SELECT dateName('day', toDate('2021-04-13')); From 1365a2c82e74a6782f3de8c16525a08c0682a9c2 Mon Sep 17 00:00:00 2001 From: dankondr Date: Tue, 27 Apr 2021 17:26:11 +0300 Subject: [PATCH 13/21] Fix include --- src/Functions/dateName.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index b493dc7f7ab..e08710f35dc 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -15,7 +15,6 @@ #include #include -#include #include #include From b8db093a656a423dbac4d4547fa0f29d7c95a345 Mon Sep 17 00:00:00 2001 From: dankondr Date: Tue, 27 Apr 2021 17:48:16 +0300 Subject: [PATCH 14/21] Fix tests --- tests/queries/0_stateless/01811_datename.sql | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/queries/0_stateless/01811_datename.sql b/tests/queries/0_stateless/01811_datename.sql index 7ecc7699462..88afc656ec1 100644 --- a/tests/queries/0_stateless/01811_datename.sql +++ b/tests/queries/0_stateless/01811_datename.sql @@ -34,13 +34,13 @@ SELECT dateName('weekday', toDate('2021-04-14')); SELECT dateName('hour', toDateTime('2021-04-14 11:22:33'), 'Europe/Moscow'), dateName('hour', toDateTime('2021-04-14 11:22:33'), 'UTC'); -SELECT dateName('day', toDate('2021-04-12')); -SELECT dateName('day', toDate('2021-04-13')); -SELECT dateName('day', toDate('2021-04-14')); -SELECT dateName('day', toDate('2021-04-15')); -SELECT dateName('day', toDate('2021-04-16')); -SELECT dateName('day', toDate('2021-04-17')); -SELECT dateName('day', toDate('2021-04-18')); +SELECT dateName('weekday', toDate('2021-04-12')); +SELECT dateName('weekday', toDate('2021-04-13')); +SELECT dateName('weekday', toDate('2021-04-14')); +SELECT dateName('weekday', toDate('2021-04-15')); +SELECT dateName('weekday', toDate('2021-04-16')); +SELECT dateName('weekday', toDate('2021-04-17')); +SELECT dateName('weekday', toDate('2021-04-18')); SELECT dateName('month', toDate('2021-01-14')); SELECT dateName('month', toDate('2021-02-14')); From e11b0b18a554742a3d66a8c0a18f03d04d68276f Mon Sep 17 00:00:00 2001 From: dankondr Date: Tue, 27 Apr 2021 18:29:23 +0300 Subject: [PATCH 15/21] Fix tests --- tests/queries/0_stateless/01811_datename.reference | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/queries/0_stateless/01811_datename.reference b/tests/queries/0_stateless/01811_datename.reference index 8bfdac794ba..df62af8ffed 100644 --- a/tests/queries/0_stateless/01811_datename.reference +++ b/tests/queries/0_stateless/01811_datename.reference @@ -25,7 +25,7 @@ April 14 15 Wednesday -14 11 +11 8 Monday Tuesday Wednesday @@ -44,4 +44,4 @@ August September October November -December \ No newline at end of file +December From 637ddc575f745f7df50247f27d3c148545711f46 Mon Sep 17 00:00:00 2001 From: dankondr Date: Tue, 27 Apr 2021 19:06:07 +0300 Subject: [PATCH 16/21] Fix tests --- tests/queries/0_stateless/01811_datename.reference | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/queries/0_stateless/01811_datename.reference b/tests/queries/0_stateless/01811_datename.reference index df62af8ffed..1b4d4b785cf 100644 --- a/tests/queries/0_stateless/01811_datename.reference +++ b/tests/queries/0_stateless/01811_datename.reference @@ -25,7 +25,7 @@ April 14 15 Wednesday -11 8 +11 8 Monday Tuesday Wednesday From 780f58abc40dc21782174c2dff03123eedc078be Mon Sep 17 00:00:00 2001 From: dankondr Date: Tue, 27 Apr 2021 19:54:05 +0300 Subject: [PATCH 17/21] Fix code style --- src/Functions/dateName.cpp | 51 +++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index e08710f35dc..2d9ecf0c124 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -1,8 +1,8 @@ -#include +#include #include #include #include -#include +#include #include #include @@ -14,8 +14,8 @@ #include -#include #include +#include #include @@ -43,10 +43,7 @@ public: static FunctionPtr create(ContextPtr) { return std::make_shared(); } - String getName() const override - { - return name; - } + String getName() const override { return name; } bool useDefaultImplementationForConstants() const override { return true; } @@ -85,12 +82,13 @@ public: } ColumnPtr executeImpl( - const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, [[maybe_unused]] size_t input_rows_count) const override + 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)) + if (!((res = executeType(arguments, result_type)) || (res = executeType(arguments, result_type)) || (res = executeType(arguments, result_type)))) throw Exception( ErrorCodes::ILLEGAL_COLUMN, @@ -181,10 +179,7 @@ private: datepart_functions.at(datepart)(target, source, timezone); } - bool isCorrectDatePart(const String &datepart) - { - return datepart_functions.find(datepart) != datepart_functions.end(); - } + bool isCorrectDatePart(const String & datepart) { return datepart_functions.find(datepart) != datepart_functions.end(); } private: const std::unordered_map datepart_functions = { @@ -213,12 +208,19 @@ private: 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" - }; + const String monthnames[12] + = {"January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December"}; writeString(target, monthnames[month - 1]); } @@ -240,9 +242,7 @@ private: 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" - }; + const String daynames[12] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; writeString(target, daynames[day - 1]); } @@ -297,8 +297,9 @@ private: } else { - throw Exception("Illegal value of second ('datetime') argument of function dateName. Check documentation.", - ErrorCodes::BAD_ARGUMENTS); + throw Exception( + "Illegal value of second ('datetime') argument of function dateName. Check documentation.", + ErrorCodes::BAD_ARGUMENTS); } } From 675d3e6c880dd009c32412f0bc5008ce95d5e3b4 Mon Sep 17 00:00:00 2001 From: dankondr Date: Wed, 28 Apr 2021 20:09:46 +0300 Subject: [PATCH 18/21] Fix code style --- src/Functions/dateName.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index 2d9ecf0c124..d1551dd2442 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -29,7 +29,8 @@ namespace ErrorCodes extern const int BAD_ARGUMENTS; } -namespace { +namespace +{ template struct ActionValueTypeMap {}; template <> struct ActionValueTypeMap { using ActionValueType = UInt16; }; From 9e83275d28423a9ae62b7c2f8b806f6ef6244459 Mon Sep 17 00:00:00 2001 From: dankondr Date: Sun, 6 Jun 2021 17:52:08 +0300 Subject: [PATCH 19/21] Make string arrays constexpr --- src/Functions/dateName.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index d1551dd2442..be4888745fb 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -209,7 +209,7 @@ private: static inline void writeMonth(char *& target, Time source, const DateLUTImpl & timezone) { const auto month = ToMonthImpl::execute(source, timezone); - const String monthnames[12] + static constexpr std::string_view monthnames[] = {"January", "February", "March", @@ -243,7 +243,7 @@ private: 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"}; + static constexpr std::string_view daynames[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; writeString(target, daynames[day - 1]); } @@ -262,7 +262,7 @@ private: writeNumber(target, ToSecondImpl::execute(source, timezone)); } - static inline void writeString(char *& target, const String & value) + static inline void writeString(char *& target, const std::string_view & value) { size_t size = value.size() + 1; /// With zero terminator memcpy(target, value.data(), size); From ac2f9dd15aa651412b20cb46a0ed3a2cfe0070e8 Mon Sep 17 00:00:00 2001 From: Maksim Kita Date: Wed, 16 Jun 2021 21:19:05 +0300 Subject: [PATCH 20/21] Updated function dateName before merge --- src/Functions/dateName.cpp | 419 +++++++++--------- src/IO/WriteHelpers.h | 1 + .../0_stateless/01811_datename.reference | 59 +-- tests/queries/0_stateless/01811_datename.sql | 112 +++-- 4 files changed, 294 insertions(+), 297 deletions(-) diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index be4888745fb..9c34b0ae55c 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -1,24 +1,19 @@ -#include +#include + +#include +#include + #include #include #include #include +#include #include #include #include -#include -#include -#include #include -#include - -#include -#include - -#include - namespace DB { namespace ErrorCodes @@ -32,10 +27,25 @@ namespace ErrorCodes namespace { -template struct ActionValueTypeMap {}; -template <> struct ActionValueTypeMap { using ActionValueType = UInt16; }; -template <> struct ActionValueTypeMap { using ActionValueType = UInt32; }; -template <> struct ActionValueTypeMap { using ActionValueType = Int64; }; +template struct DataTypeToTimeTypeMap {}; + +template <> struct DataTypeToTimeTypeMap +{ + using TimeType = UInt16; +}; + +template <> struct DataTypeToTimeTypeMap +{ + using TimeType = UInt32; +}; + +template <> struct DataTypeToTimeTypeMap +{ + using TimeType = Int64; +}; + +template +using DateTypeToTimeType = typename DataTypeToTimeTypeMap::TimeType; class FunctionDateNameImpl : public IFunction { @@ -61,24 +71,30 @@ public: "Number of arguments for function {} doesn't match: passed {}", getName(), toString(arguments.size())); + if (!WhichDataType(arguments[0].type).isString()) throw Exception( ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of 1 argument of function {}. Must be string", arguments[0].type->getName(), getName()); - if (!WhichDataType(arguments[1].type).isDateOrDateTime()) + + WhichDataType first_argument_type(arguments[1].type); + + if (!(first_argument_type.isDate() || first_argument_type.isDateTime() || first_argument_type.isDateTime64())) throw Exception( ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of 2 argument of function {}. Must be a date or a date with time", arguments[1].type->getName(), getName()); + if (arguments.size() == 3 && !WhichDataType(arguments[2].type).isString()) throw Exception( ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of 3 argument of function {}. Must be string", arguments[2].type->getName(), getName()); + return std::make_shared(); } @@ -89,8 +105,9 @@ public: { ColumnPtr res; - if (!((res = executeType(arguments, result_type)) || (res = executeType(arguments, result_type)) - || (res = executeType(arguments, result_type)))) + if (!((res = executeType(arguments, result_type)) + || (res = executeType(arguments, result_type)) + || (res = executeType(arguments, result_type)))) throw Exception( ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of function {], must be Date or DateTime.", @@ -107,24 +124,15 @@ public: if (!times) return nullptr; - const ColumnConst * datepart_column = checkAndGetColumnConst(arguments[0].column.get()); - if (!datepart_column) + const ColumnConst * date_part_column = checkAndGetColumnConst(arguments[0].column.get()); + if (!date_part_column) throw Exception( ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first ('datepart') argument of function {}. Must be constant string.", arguments[0].column->getName(), getName()); - using T = typename ActionValueTypeMap::ActionValueType; - auto datepart_writer = DatePartWriter(); - String datepart = datepart_column->getValue(); - - if (!datepart_writer.isCorrectDatePart(datepart)) - throw Exception( - ErrorCodes::BAD_ARGUMENTS, - "Illegal value {} of first ('format') argument of function {}. Check documentation", - datepart, - getName()); + String date_part = date_part_column->getValue(); const DateLUTImpl * time_zone_tmp; if (std::is_same_v || std::is_same_v) @@ -132,203 +140,210 @@ public: else time_zone_tmp = &DateLUT::instance(); - const auto & vec = times->getData(); + const auto & times_data = times->getData(); const DateLUTImpl & time_zone = *time_zone_tmp; UInt32 scale [[maybe_unused]] = 0; if constexpr (std::is_same_v) { - scale = vec.getScale(); + scale = times_data.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 result_column = ColumnString::create(); + auto & result_column_data = result_column->getChars(); + auto & result_column_offsets = result_column->getOffsets(); - auto * begin = reinterpret_cast(dst_data.data()); - auto * pos = begin; + /* longest possible word 'Wednesday' with zero terminator */ + static constexpr size_t longest_word_length = 9 + 1; - for (size_t i = 0; i < vec.size(); ++i) + result_column_data.resize_fill(times_data.size() * longest_word_length); + result_column_offsets.resize(times_data.size()); + + auto * begin = reinterpret_cast(result_column_data.data()); + + WriteBuffer buffer(begin, result_column_data.size()); + + using TimeType = DateTypeToTimeType; + callOnDatePartWriter(date_part, [&](const auto & writer) { - if constexpr (std::is_same_v) + for (size_t i = 0; i < times_data.size(); ++i) { - // 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); + if constexpr (std::is_same_v) + { + const auto components = DecimalUtils::split(times_data[i], scale); + writer.write(buffer, static_cast(components.whole), time_zone); + } + else + { + writer.write(buffer, times_data[i], time_zone); + } + + /// Null terminator + ++buffer.position(); + result_column_offsets[i] = buffer.position() - begin; } - else - { - datepart_writer.writeDatePart(pos, datepart, vec[i], time_zone); - } - dst_offsets[i] = pos - begin; - ++pos; - } - dst_data.resize(pos - begin); - return col_res; + }); + + result_column_data.resize(buffer.position() - begin); + + return result_column; } private: + template - class DatePartWriter + struct YearWriter { - public: - void writeDatePart(char *& target, const String & datepart, Time source, const DateLUTImpl & timezone) + static void write(WriteBuffer & buffer, 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); - static constexpr std::string_view monthnames[] - = {"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); - static constexpr std::string_view daynames[] = {"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 std::string_view & 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 dateName. 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 + '0'; - } - - template - static inline void writeNumber4(char * p, T v) - { - writeNumber2(p, v / 100); - writeNumber2(p + 2, v % 100); + writeText(ToYearImpl::execute(source, timezone), buffer); } }; + + template + struct QuarterWriter + { + static inline void write(WriteBuffer & buffer, Time source, const DateLUTImpl & timezone) + { + writeText(ToQuarterImpl::execute(source, timezone), buffer); + } + }; + + template + struct MonthWriter + { + static inline void write(WriteBuffer & buffer, Time source, const DateLUTImpl & timezone) + { + const auto month = ToMonthImpl::execute(source, timezone); + static constexpr std::string_view month_names[] = + { + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" + }; + + writeText(month_names[month - 1], buffer); + } + }; + + template + struct WeekWriter + { + static inline void write(WriteBuffer & buffer, Time source, const DateLUTImpl & timezone) + { + writeText(ToISOWeekImpl::execute(source, timezone), buffer); + } + }; + + template + struct DayOfYearWriter + { + static inline void write(WriteBuffer & buffer, Time source, const DateLUTImpl & timezone) + { + writeText(ToDayOfYearImpl::execute(source, timezone), buffer); + } + }; + + template + struct DayWriter + { + static inline void write(WriteBuffer & buffer, Time source, const DateLUTImpl & timezone) + { + writeText(ToDayOfMonthImpl::execute(source, timezone), buffer); + } + }; + + template + struct WeekDayWriter + { + static inline void write(WriteBuffer & buffer, Time source, const DateLUTImpl & timezone) + { + const auto day = ToDayOfWeekImpl::execute(source, timezone); + static constexpr std::string_view day_names[] = + { + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday" + }; + + writeText(day_names[day - 1], buffer); + } + }; + + template + struct HourWriter + { + static inline void write(WriteBuffer & buffer, Time source, const DateLUTImpl & timezone) + { + writeText(ToHourImpl::execute(source, timezone), buffer); + } + }; + + template + struct MinuteWriter + { + static inline void write(WriteBuffer & buffer, Time source, const DateLUTImpl & timezone) + { + writeText(ToMinuteImpl::execute(source, timezone), buffer); + } + }; + + template + struct SecondWriter + { + static inline void write(WriteBuffer & buffer, Time source, const DateLUTImpl & timezone) + { + writeText(ToSecondImpl::execute(source, timezone), buffer); + } + }; + + template + void callOnDatePartWriter(const String & date_part, Call && call) const + { + if (date_part == "year") + std::forward(call)(YearWriter