From d0ed96aa045affc657380e194b594e4a27c12843 Mon Sep 17 00:00:00 2001 From: CurtizJ Date: Mon, 24 Sep 2018 22:08:11 +0300 Subject: [PATCH 1/6] better code --- dbms/src/Interpreters/Context.cpp | 10 +++++----- dbms/src/Interpreters/Context.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index 9452fb9d5dd..8239841897d 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -897,18 +897,18 @@ StoragePtr Context::executeTableFunction(const ASTPtr & table_expression) } -DDLGuard::DDLGuard(Map & map_, std::mutex & guards_mutex_, std::unique_lock && lock, const String & elem) - : map(map_), guards_mutex(guards_mutex_) +DDLGuard::DDLGuard(Map & map_, std::unique_lock guards_lock_, const String & elem) + : map(map_), guards_lock(std::move(guards_lock_)) { it = map.emplace(elem, Entry{std::make_unique(), 0}).first; ++it->second.counter; - lock.unlock(); + guards_lock.unlock(); table_lock = std::unique_lock(*it->second.mutex); } DDLGuard::~DDLGuard() { - std::lock_guard lock(guards_mutex); + guards_lock.lock(); --it->second.counter; if (!it->second.counter) { @@ -920,7 +920,7 @@ DDLGuard::~DDLGuard() std::unique_ptr Context::getDDLGuard(const String & database, const String & table) const { std::unique_lock lock(shared->ddl_guards_mutex); - return std::make_unique(shared->ddl_guards[database], shared->ddl_guards_mutex, std::move(lock), table); + return std::make_unique(shared->ddl_guards[database], std::move(lock), table); } diff --git a/dbms/src/Interpreters/Context.h b/dbms/src/Interpreters/Context.h index 0237ea804c2..7a4682ef205 100644 --- a/dbms/src/Interpreters/Context.h +++ b/dbms/src/Interpreters/Context.h @@ -481,13 +481,13 @@ public: /// NOTE: using std::map here (and not std::unordered_map) to avoid iterator invalidation on insertion. using Map = std::map; - DDLGuard(Map & map_, std::mutex & guards_mutex_, std::unique_lock && guards_lock, const String & elem); + DDLGuard(Map & map_, std::unique_lock guards_lock_, const String & elem); ~DDLGuard(); private: Map & map; Map::iterator it; - std::mutex & guards_mutex; + std::unique_lock guards_lock; std::unique_lock table_lock; }; From 96ceca6c059132035a162198d74cc3546c0f1b79 Mon Sep 17 00:00:00 2001 From: chertus Date: Tue, 25 Sep 2018 19:03:50 +0300 Subject: [PATCH 2/6] fix decimal to int, int to decimal convertion [issue-3177] --- dbms/src/DataTypes/DataTypesDecimal.h | 101 ++++++++++++------ dbms/src/Functions/FunctionsConversion.h | 22 ++-- .../0_stateless/00700_decimal_casts.reference | 36 +++++++ .../0_stateless/00700_decimal_casts.sql | 90 ++++++++++++++++ 4 files changed, 210 insertions(+), 39 deletions(-) diff --git a/dbms/src/DataTypes/DataTypesDecimal.h b/dbms/src/DataTypes/DataTypesDecimal.h index 8b65fbfe388..c93f209bffe 100644 --- a/dbms/src/DataTypes/DataTypesDecimal.h +++ b/dbms/src/DataTypes/DataTypesDecimal.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include #include #include @@ -12,9 +12,9 @@ namespace DB namespace ErrorCodes { - extern const int LOGICAL_ERROR; extern const int ARGUMENT_OUT_OF_BOUND; extern const int CANNOT_CONVERT_TYPE; + extern const int DECIMAL_OVERFLOW; } class Context; @@ -39,9 +39,7 @@ DataTypePtr createDecimal(UInt64 precision, UInt64 scale); /// Int128 38 /// Operation between two decimals leads to Decimal(P, S), where /// P is one of (9, 18, 38); equals to the maximum precision for the biggest underlying type of operands. -/// S is maximum scale of operands. -/// -/// NOTE: It's possible to set scale as a template parameter then most of functions become static. +/// S is maximum scale of operands. The allowed valuas are [0, precision] template class DataTypeDecimal final : public DataTypeWithSimpleSerialization { @@ -156,7 +154,7 @@ public: private: const UInt32 precision; - const UInt32 scale; /// TODO: should we support scales out of [0, precision]? + const UInt32 scale; }; @@ -221,49 +219,86 @@ template <> constexpr bool IsDataTypeDecimal> = true; template <> constexpr bool IsDataTypeDecimal> = true; template <> constexpr bool IsDataTypeDecimal> = true; +template constexpr bool IsDataTypeDecimalOrNumber = IsDataTypeDecimal || IsDataTypeNumber; + template inline std::enable_if_t && IsDataTypeDecimal, typename ToDataType::FieldType> convertDecimals(const typename FromDataType::FieldType & value, UInt32 scale_from, UInt32 scale_to) { - ToDataType type_to(ToDataType::maxPrecision(), scale_to); - FromDataType type_from(FromDataType::maxPrecision(), scale_from); + using FromFieldType = typename FromDataType::FieldType; + using ToFieldType = typename ToDataType::FieldType; + using MaxFieldType = std::conditional_t<(sizeof(FromFieldType) > sizeof(ToFieldType)), FromFieldType, ToFieldType>; + using MaxNativeType = typename MaxFieldType::NativeType; - if (scale_from > scale_to) + MaxNativeType converted_value; + if (scale_to > scale_from) { - typename FromDataType::FieldType factor = type_from.scaleFactorFor(type_to, false); - return value / factor; + converted_value = DataTypeDecimal::getScaleMultiplier(scale_to - scale_from); + if (common::mulOverflow(static_cast(value), converted_value, converted_value)) + throw Exception("Decimal convert overflow", ErrorCodes::DECIMAL_OVERFLOW); } else + converted_value = value / DataTypeDecimal::getScaleMultiplier(scale_from - scale_to); + + if constexpr (sizeof(FromFieldType) > sizeof(ToFieldType)) { - typename ToDataType::FieldType factor = type_to.scaleFactorFor(type_from, false); - return value * factor; + if (converted_value < std::numeric_limits::min() || + converted_value > std::numeric_limits::max()) + throw Exception("Decimal convert overflow", ErrorCodes::DECIMAL_OVERFLOW); + } + + return converted_value; +} + +template +inline std::enable_if_t && IsDataTypeNumber, typename ToDataType::FieldType> +convertFromDecimal(const typename FromDataType::FieldType & value, UInt32 scale) +{ + using FromFieldType = typename FromDataType::FieldType; + using ToFieldType = typename ToDataType::FieldType; + + if constexpr (std::is_floating_point_v) + return static_cast(value) / FromDataType::getScaleMultiplier(scale); + else + { + FromFieldType converted_value = convertDecimals(value, scale, 0); + + if constexpr (sizeof(FromFieldType) > sizeof(ToFieldType) || !std::numeric_limits::is_signed) + { + if constexpr (std::numeric_limits::is_signed) + { + if (converted_value < std::numeric_limits::min() || + converted_value > std::numeric_limits::max()) + throw Exception("Decimal convert overflow", ErrorCodes::DECIMAL_OVERFLOW); + } + else + { + using CastIntType = std::conditional_t, Int128, Int64>; + + if (converted_value < 0 || + converted_value > static_cast(std::numeric_limits::max())) + throw Exception("Decimal convert overflow", ErrorCodes::DECIMAL_OVERFLOW); + } + } + return converted_value; } } template -inline std::enable_if_t && !IsDataTypeDecimal, typename ToDataType::FieldType> -convertFromDecimal(const typename FromDataType::FieldType & value, UInt32 scale [[maybe_unused]]) +inline std::enable_if_t && IsDataTypeDecimal, typename ToDataType::FieldType> +convertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale) { - if (scale > FromDataType::maxPrecision()) - throw Exception("Wrong decimal scale", ErrorCodes::LOGICAL_ERROR); + using FromFieldType = typename FromDataType::FieldType; - if constexpr (!std::is_same_v>) - throw Exception("Illegal convertion from decimal", ErrorCodes::CANNOT_CONVERT_TYPE); - else - return static_cast(value) / FromDataType::getScaleMultiplier(scale); -} - -template -inline std::enable_if_t && IsDataTypeDecimal, typename ToDataType::FieldType> -convertToDecimal(const typename FromDataType::FieldType & value, UInt32 scale [[maybe_unused]]) -{ - if (scale > ToDataType::maxPrecision()) - throw Exception("Wrong decimal scale", ErrorCodes::LOGICAL_ERROR); - - if constexpr (!std::is_same_v>) - throw Exception("Illegal convertion to decimal", ErrorCodes::CANNOT_CONVERT_TYPE); - else + if constexpr (std::is_floating_point_v) return value * ToDataType::getScaleMultiplier(scale); + else + { + if constexpr (std::is_same_v) + if (value > static_cast(std::numeric_limits::max())) + return convertDecimals, ToDataType>(value, 0, scale); + return convertDecimals, ToDataType>(value, 0, scale); + } } } diff --git a/dbms/src/Functions/FunctionsConversion.h b/dbms/src/Functions/FunctionsConversion.h index 283da2a99e7..9ce2c078fa4 100644 --- a/dbms/src/Functions/FunctionsConversion.h +++ b/dbms/src/Functions/FunctionsConversion.h @@ -107,6 +107,13 @@ struct ConvertImpl using ColVecFrom = std::conditional_t, ColumnDecimal, ColumnVector>; using ColVecTo = std::conditional_t, ColumnDecimal, ColumnVector>; + if constexpr (IsDataTypeDecimal || IsDataTypeDecimal) + { + if constexpr (!IsDataTypeDecimalOrNumber || !IsDataTypeDecimalOrNumber) + throw Exception("Illegal column " + named_from.column->getName() + " of first argument of function " + Name::name, + ErrorCodes::ILLEGAL_COLUMN); + } + if (const ColVecFrom * col_from = checkAndGetColumn(named_from.column.get())) { typename ColVecTo::MutablePtr col_to = nullptr; @@ -125,12 +132,15 @@ struct ConvertImpl for (size_t i = 0; i < size; ++i) { - if constexpr (IsDataTypeDecimal && IsDataTypeDecimal) - vec_to[i] = convertDecimals(vec_from[i], vec_from.getScale(), vec_to.getScale()); - else if constexpr (IsDataTypeDecimal) - vec_to[i] = convertFromDecimal(vec_from[i], vec_from.getScale()); - else if constexpr (IsDataTypeDecimal) - vec_to[i] = convertToDecimal(vec_from[i], vec_to.getScale()); + if constexpr (IsDataTypeDecimal || IsDataTypeDecimal) + { + if constexpr (IsDataTypeDecimal && IsDataTypeDecimal) + vec_to[i] = convertDecimals(vec_from[i], vec_from.getScale(), vec_to.getScale()); + else if constexpr (IsDataTypeDecimal && IsDataTypeNumber) + vec_to[i] = convertFromDecimal(vec_from[i], vec_from.getScale()); + else if constexpr (IsDataTypeNumber && IsDataTypeDecimal) + vec_to[i] = convertToDecimal(vec_from[i], vec_to.getScale()); + } else vec_to[i] = static_cast(vec_from[i]); } diff --git a/dbms/tests/queries/0_stateless/00700_decimal_casts.reference b/dbms/tests/queries/0_stateless/00700_decimal_casts.reference index 6a574b6245b..0a4aba94bcc 100644 --- a/dbms/tests/queries/0_stateless/00700_decimal_casts.reference +++ b/dbms/tests/queries/0_stateless/00700_decimal_casts.reference @@ -129,3 +129,39 @@ 12345678901234567890123456789012345678 0.123456789 0.123456789123456789 0.12345678901234567890123456789012345678 +1234567890.0000000000000000000000000000 1234567890.00000000000000000000000000000 1234567890.00000000000000000000000000000 +1234567890.00000000 1234567890.000000000 1234567890.000000000 +12345678.0 12345678.00 12345678.00 +9223372036854775807.000000 9223372036854775807 -9223372036854775807 +9223372036854775800 9223372036854775800 -9223372036854775800 +92233720368547758.00 92233720368547758 -92233720368547758 +2147483647.0000000000 2147483647 -2147483647 +2147483647.00 2147483647 -2147483647 +92233720368547757.99 92233720368547757 -92233720368547757 +2147483640.99 2147483640 -2147483640 +-0.90000000 0 +-0.90000000 0 +-0.90000000 0 +-0.8000 0 +-0.8000 0 +-0.8000 0 +-0.70 0 +-0.70 0 +-0.70 0 +-0.600000 0 +-0.600000 0 +-0.600000 0 +18446744073709551615 18446744073709551615 +18446744073709551615.00000000 18446744073709551615 +4294967295 4294967295 +4294967295.0000000000 4294967295 +4294967295 4294967295 +4294967295.0000 4294967295 +65535 65535 +65535.0000000000 65535 +65535 65535 +65535.0000 65535 +2147483647 2147483647 +-2147483647 -2147483647 +2147483647 2147483647 +9223372036854775807 9223372036854775807 diff --git a/dbms/tests/queries/0_stateless/00700_decimal_casts.sql b/dbms/tests/queries/0_stateless/00700_decimal_casts.sql index 111dc5fb1cb..dbfe1badedb 100644 --- a/dbms/tests/queries/0_stateless/00700_decimal_casts.sql +++ b/dbms/tests/queries/0_stateless/00700_decimal_casts.sql @@ -150,4 +150,94 @@ SELECT CAST('0.123456789', 'Decimal(9,8)'); -- { serverError 69 } SELECT CAST('0.123456789123456789', 'Decimal(18,17)'); -- { serverError 69 } SELECT CAST('0.12345678901234567890123456789012345678', 'Decimal(38,37)'); -- { serverError 69 } +SELECT toDecimal128('1234567890', 28) AS x, toDecimal128(x, 29), toDecimal128(toDecimal128('1234567890', 28), 29); +SELECT toDecimal128(toDecimal128('1234567890', 28), 30); -- { serverError 407 } + +SELECT toDecimal64('1234567890', 8) AS x, toDecimal64(x, 9), toDecimal64(toDecimal64('1234567890', 8), 9); +SELECT toDecimal64(toDecimal64('1234567890', 8), 10); -- { serverError 407 } + +SELECT toDecimal32('12345678', 1) AS x, toDecimal32(x, 2), toDecimal32(toDecimal32('12345678', 1), 2); +SELECT toDecimal32(toDecimal32('12345678', 1), 3); -- { serverError 407 } + +SELECT toDecimal64(toDecimal64('92233720368547758.1', 1), 2); -- { serverError 407 } +SELECT toDecimal64(toDecimal64('-92233720368547758.1', 1), 2); -- { serverError 407 } + +SELECT toDecimal128('9223372036854775807', 6) AS x, toInt64(x), toInt64(-x); +SELECT toDecimal128('9223372036854775809', 6) AS x, toInt64(x); -- { serverError 407 } +SELECT toDecimal128('9223372036854775809', 6) AS x, toInt64(-x); -- { serverError 407 } +SELECT toDecimal64('922337203685477580', 0) * 10 AS x, toInt64(x), toInt64(-x); +SELECT toDecimal64(toDecimal64('92233720368547758.0', 1), 2) AS x, toInt64(x), toInt64(-x); + +SELECT toDecimal128('2147483647', 10) AS x, toInt32(x), toInt32(-x); +SELECT toDecimal128('2147483649', 10) AS x, toInt32(x), toInt32(-x); -- { serverError 407 } +SELECT toDecimal64('2147483647', 2) AS x, toInt32(x), toInt32(-x); +SELECT toDecimal64('2147483649', 2) AS x, toInt32(x), toInt32(-x); -- { serverError 407 } + +SELECT toDecimal128('92233720368547757.99', 2) AS x, toInt64(x), toInt64(-x); +SELECT toDecimal64('2147483640.99', 2) AS x, toInt32(x), toInt32(-x); + +SELECT toDecimal128('-0.9', 8) AS x, toUInt64(x); +SELECT toDecimal64('-0.9', 8) AS x, toUInt64(x); +SELECT toDecimal32('-0.9', 8) AS x, toUInt64(x); + +SELECT toDecimal128('-0.8', 4) AS x, toUInt32(x); +SELECT toDecimal64('-0.8', 4) AS x, toUInt32(x); +SELECT toDecimal32('-0.8', 4) AS x, toUInt32(x); + +SELECT toDecimal128('-0.7', 2) AS x, toUInt16(x); +SELECT toDecimal64('-0.7', 2) AS x, toUInt16(x); +SELECT toDecimal32('-0.7', 2) AS x, toUInt16(x); + +SELECT toDecimal128('-0.6', 6) AS x, toUInt8(x); +SELECT toDecimal64('-0.6', 6) AS x, toUInt8(x); +SELECT toDecimal32('-0.6', 6) AS x, toUInt8(x); + +SELECT toDecimal128('-1', 7) AS x, toUInt64(x); -- { serverError 407 } +SELECT toDecimal128('-1', 7) AS x, toUInt32(x); -- { serverError 407 } +SELECT toDecimal128('-1', 7) AS x, toUInt16(x); -- { serverError 407 } +SELECT toDecimal128('-1', 7) AS x, toUInt8(x); -- { serverError 407 } + +SELECT toDecimal64('-1', 5) AS x, toUInt64(x); -- { serverError 407 } +SELECT toDecimal64('-1', 5) AS x, toUInt32(x); -- { serverError 407 } +SELECT toDecimal64('-1', 5) AS x, toUInt16(x); -- { serverError 407 } +SELECT toDecimal64('-1', 5) AS x, toUInt8(x); -- { serverError 407 } + +SELECT toDecimal32('-1', 3) AS x, toUInt64(x); -- { serverError 407 } +SELECT toDecimal32('-1', 3) AS x, toUInt32(x); -- { serverError 407 } +SELECT toDecimal32('-1', 3) AS x, toUInt16(x); -- { serverError 407 } +SELECT toDecimal32('-1', 3) AS x, toUInt8(x); -- { serverError 407 } + +SELECT toDecimal128('18446744073709551615', 0) AS x, toUInt64(x); +SELECT toDecimal128('18446744073709551616', 0) AS x, toUInt64(x); -- { serverError 407 } +SELECT toDecimal128('18446744073709551615', 8) AS x, toUInt64(x); +SELECT toDecimal128('18446744073709551616', 8) AS x, toUInt64(x); -- { serverError 407 } + +SELECT toDecimal128('4294967295', 0) AS x, toUInt32(x); +SELECT toDecimal128('4294967296', 0) AS x, toUInt32(x); -- { serverError 407 } +SELECT toDecimal128('4294967295', 10) AS x, toUInt32(x); +SELECT toDecimal128('4294967296', 10) AS x, toUInt32(x); -- { serverError 407 } +SELECT toDecimal64('4294967295', 0) AS x, toUInt32(x); +SELECT toDecimal64('4294967296', 0) AS x, toUInt32(x); -- { serverError 407 } +SELECT toDecimal64('4294967295', 4) AS x, toUInt32(x); +SELECT toDecimal64('4294967296', 4) AS x, toUInt32(x); -- { serverError 407 } + +SELECT toDecimal128('65535', 0) AS x, toUInt16(x); +SELECT toDecimal128('65536', 0) AS x, toUInt16(x); -- { serverError 407 } +SELECT toDecimal128('65535', 10) AS x, toUInt16(x); +SELECT toDecimal128('65536', 10) AS x, toUInt16(x); -- { serverError 407 } +SELECT toDecimal64('65535', 0) AS x, toUInt16(x); +SELECT toDecimal64('65536', 0) AS x, toUInt16(x); -- { serverError 407 } +SELECT toDecimal64('65535', 4) AS x, toUInt16(x); +SELECT toDecimal64('65536', 4) AS x, toUInt16(x); -- { serverError 407 } + +SELECT toInt64('2147483647') AS x, toDecimal32(x, 0); +SELECT toInt64('-2147483647') AS x, toDecimal32(x, 0); +SELECT toUInt64('2147483647') AS x, toDecimal32(x, 0); +SELECT toInt64('2147483649') AS x, toDecimal32(x, 0); -- { serverError 407 } +SELECT toInt64('-2147483649') AS x, toDecimal32(x, 0); -- { serverError 407 } +SELECT toUInt64('2147483649') AS x, toDecimal32(x, 0); -- { serverError 407 } + +SELECT toUInt64('9223372036854775807') AS x, toDecimal64(x, 0); +SELECT toUInt64('9223372036854775809') AS x, toDecimal64(x, 0); -- { serverError 407 } + DROP TABLE IF EXISTS test.decimal; From c449319a3bf6c92d7c06f91e613d21d749f57dbf Mon Sep 17 00:00:00 2001 From: alexey-milovidov Date: Tue, 25 Sep 2018 21:18:50 +0300 Subject: [PATCH 3/6] Update string_search_functions.md --- docs/ru/query_language/functions/string_search_functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/query_language/functions/string_search_functions.md b/docs/ru/query_language/functions/string_search_functions.md index 99bdef12f29..a55c8f18084 100644 --- a/docs/ru/query_language/functions/string_search_functions.md +++ b/docs/ru/query_language/functions/string_search_functions.md @@ -15,7 +15,7 @@ Для поиска без учета регистра используйте функцию `positionCaseInsensitiveUTF8`. ## match(haystack, pattern) -Проверка строки на соответствие регулярному выражению pattern. Регулярное выражение re2. +Проверка строки на соответствие регулярному выражению pattern. Регулярное выражение **re2**. Синтаксис регулярных выражений **re2** является более ограниченным по сравнению с регулярными выражениями **Perl**. [Подробнее](https://github.com/google/re2/wiki/Syntax). Возвращает 0 (если не соответствует) или 1 (если соответствует). Обратите внимание, что для экранирования в регулярном выражении, используется символ `\` (обратный слеш). Этот же символ используется для экранирования в строковых литералах. Поэтому, чтобы экранировать символ в регулярном выражении, необходимо написать в строковом литерале \\ (два обратных слеша). From 0dd4bb43a3247d6b4157c50b0fa3b062545170c4 Mon Sep 17 00:00:00 2001 From: alexey-milovidov Date: Tue, 25 Sep 2018 21:19:15 +0300 Subject: [PATCH 4/6] Update string_search_functions.md --- docs/ru/query_language/functions/string_search_functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/query_language/functions/string_search_functions.md b/docs/ru/query_language/functions/string_search_functions.md index a55c8f18084..125c57a6138 100644 --- a/docs/ru/query_language/functions/string_search_functions.md +++ b/docs/ru/query_language/functions/string_search_functions.md @@ -15,7 +15,7 @@ Для поиска без учета регистра используйте функцию `positionCaseInsensitiveUTF8`. ## match(haystack, pattern) -Проверка строки на соответствие регулярному выражению pattern. Регулярное выражение **re2**. Синтаксис регулярных выражений **re2** является более ограниченным по сравнению с регулярными выражениями **Perl**. [Подробнее](https://github.com/google/re2/wiki/Syntax). +Проверка строки на соответствие регулярному выражению pattern. Регулярное выражение **re2**. Синтаксис регулярных выражений **re2** является более ограниченным по сравнению с регулярными выражениями **Perl** ([подробнее](https://github.com/google/re2/wiki/Syntax)). Возвращает 0 (если не соответствует) или 1 (если соответствует). Обратите внимание, что для экранирования в регулярном выражении, используется символ `\` (обратный слеш). Этот же символ используется для экранирования в строковых литералах. Поэтому, чтобы экранировать символ в регулярном выражении, необходимо написать в строковом литерале \\ (два обратных слеша). From 27b7654abc9d9ade0c91cbc352cd6205dc97d588 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 26 Sep 2018 03:31:40 +0300 Subject: [PATCH 5/6] Every function in its own file, part 5 [#CLICKHOUSE-2] --- dbms/src/Functions/DateTimeTransforms.h | 651 ++++++++++++++++++ .../FunctionDateOrDateTimeAddInterval.h | 308 +++++++++ .../FunctionDateOrDateTimeToSomething.h | 125 ++++ dbms/src/Functions/FunctionsConversion.h | 2 +- dbms/src/Functions/addDays.cpp | 18 + dbms/src/Functions/addHours.cpp | 18 + dbms/src/Functions/addMinutes.cpp | 18 + dbms/src/Functions/addMonths.cpp | 18 + dbms/src/Functions/addSeconds.cpp | 18 + dbms/src/Functions/addWeeks.cpp | 18 + dbms/src/Functions/addYears.cpp | 18 + dbms/src/Functions/dateDiff.cpp | 220 ++++++ .../extractTimeZoneFromFunctionArguments.cpp | 69 ++ .../extractTimeZoneFromFunctionArguments.h | 17 + dbms/src/Functions/formatDateTime.cpp | 571 +++++++++++++++ dbms/src/Functions/now.cpp | 44 ++ dbms/src/Functions/subtractDays.cpp | 18 + dbms/src/Functions/subtractHours.cpp | 18 + dbms/src/Functions/subtractMinutes.cpp | 18 + dbms/src/Functions/subtractMonths.cpp | 18 + dbms/src/Functions/subtractSeconds.cpp | 18 + dbms/src/Functions/subtractWeeks.cpp | 18 + dbms/src/Functions/subtractYears.cpp | 18 + dbms/src/Functions/timeSlot.cpp | 21 + dbms/src/Functions/timeSlots.cpp | 182 +++++ dbms/src/Functions/toDayOfMonth.cpp | 20 + dbms/src/Functions/toDayOfWeek.cpp | 20 + dbms/src/Functions/toDayOfYear.cpp | 20 + dbms/src/Functions/toHour.cpp | 20 + dbms/src/Functions/toISOWeek.cpp | 20 + dbms/src/Functions/toISOYear.cpp | 20 + dbms/src/Functions/toMinute.cpp | 20 + dbms/src/Functions/toMonday.cpp | 20 + dbms/src/Functions/toMonth.cpp | 20 + dbms/src/Functions/toQuarter.cpp | 20 + dbms/src/Functions/toRelativeDayNum.cpp | 20 + dbms/src/Functions/toRelativeHourNum.cpp | 20 + dbms/src/Functions/toRelativeMinuteNum.cpp | 20 + dbms/src/Functions/toRelativeMonthNum.cpp | 20 + dbms/src/Functions/toRelativeQuarterNum.cpp | 20 + dbms/src/Functions/toRelativeSecondNum.cpp | 20 + dbms/src/Functions/toRelativeWeekNum.cpp | 20 + dbms/src/Functions/toRelativeYearNum.cpp | 20 + dbms/src/Functions/toSecond.cpp | 20 + dbms/src/Functions/toStartOfDay.cpp | 20 + .../src/Functions/toStartOfFifteenMinutes.cpp | 20 + dbms/src/Functions/toStartOfFiveMinute.cpp | 20 + dbms/src/Functions/toStartOfHour.cpp | 20 + dbms/src/Functions/toStartOfISOYear.cpp | 20 + dbms/src/Functions/toStartOfMinute.cpp | 20 + dbms/src/Functions/toStartOfMonth.cpp | 20 + dbms/src/Functions/toStartOfQuarter.cpp | 20 + dbms/src/Functions/toStartOfYear.cpp | 20 + dbms/src/Functions/toTime.cpp | 20 + dbms/src/Functions/toTimeZone.cpp | 59 ++ dbms/src/Functions/toYYYYMM.cpp | 20 + dbms/src/Functions/toYYYYMMDD.cpp | 20 + dbms/src/Functions/toYYYYMMDDhhmmss.cpp | 20 + dbms/src/Functions/toYear.cpp | 20 + dbms/src/Functions/today.cpp | 45 ++ dbms/src/Functions/yesterday.cpp | 45 ++ 61 files changed, 3270 insertions(+), 1 deletion(-) create mode 100644 dbms/src/Functions/DateTimeTransforms.h create mode 100644 dbms/src/Functions/FunctionDateOrDateTimeAddInterval.h create mode 100644 dbms/src/Functions/FunctionDateOrDateTimeToSomething.h create mode 100644 dbms/src/Functions/addDays.cpp create mode 100644 dbms/src/Functions/addHours.cpp create mode 100644 dbms/src/Functions/addMinutes.cpp create mode 100644 dbms/src/Functions/addMonths.cpp create mode 100644 dbms/src/Functions/addSeconds.cpp create mode 100644 dbms/src/Functions/addWeeks.cpp create mode 100644 dbms/src/Functions/addYears.cpp create mode 100644 dbms/src/Functions/dateDiff.cpp create mode 100644 dbms/src/Functions/extractTimeZoneFromFunctionArguments.cpp create mode 100644 dbms/src/Functions/extractTimeZoneFromFunctionArguments.h create mode 100644 dbms/src/Functions/formatDateTime.cpp create mode 100644 dbms/src/Functions/now.cpp create mode 100644 dbms/src/Functions/subtractDays.cpp create mode 100644 dbms/src/Functions/subtractHours.cpp create mode 100644 dbms/src/Functions/subtractMinutes.cpp create mode 100644 dbms/src/Functions/subtractMonths.cpp create mode 100644 dbms/src/Functions/subtractSeconds.cpp create mode 100644 dbms/src/Functions/subtractWeeks.cpp create mode 100644 dbms/src/Functions/subtractYears.cpp create mode 100644 dbms/src/Functions/timeSlot.cpp create mode 100644 dbms/src/Functions/timeSlots.cpp create mode 100644 dbms/src/Functions/toDayOfMonth.cpp create mode 100644 dbms/src/Functions/toDayOfWeek.cpp create mode 100644 dbms/src/Functions/toDayOfYear.cpp create mode 100644 dbms/src/Functions/toHour.cpp create mode 100644 dbms/src/Functions/toISOWeek.cpp create mode 100644 dbms/src/Functions/toISOYear.cpp create mode 100644 dbms/src/Functions/toMinute.cpp create mode 100644 dbms/src/Functions/toMonday.cpp create mode 100644 dbms/src/Functions/toMonth.cpp create mode 100644 dbms/src/Functions/toQuarter.cpp create mode 100644 dbms/src/Functions/toRelativeDayNum.cpp create mode 100644 dbms/src/Functions/toRelativeHourNum.cpp create mode 100644 dbms/src/Functions/toRelativeMinuteNum.cpp create mode 100644 dbms/src/Functions/toRelativeMonthNum.cpp create mode 100644 dbms/src/Functions/toRelativeQuarterNum.cpp create mode 100644 dbms/src/Functions/toRelativeSecondNum.cpp create mode 100644 dbms/src/Functions/toRelativeWeekNum.cpp create mode 100644 dbms/src/Functions/toRelativeYearNum.cpp create mode 100644 dbms/src/Functions/toSecond.cpp create mode 100644 dbms/src/Functions/toStartOfDay.cpp create mode 100644 dbms/src/Functions/toStartOfFifteenMinutes.cpp create mode 100644 dbms/src/Functions/toStartOfFiveMinute.cpp create mode 100644 dbms/src/Functions/toStartOfHour.cpp create mode 100644 dbms/src/Functions/toStartOfISOYear.cpp create mode 100644 dbms/src/Functions/toStartOfMinute.cpp create mode 100644 dbms/src/Functions/toStartOfMonth.cpp create mode 100644 dbms/src/Functions/toStartOfQuarter.cpp create mode 100644 dbms/src/Functions/toStartOfYear.cpp create mode 100644 dbms/src/Functions/toTime.cpp create mode 100644 dbms/src/Functions/toTimeZone.cpp create mode 100644 dbms/src/Functions/toYYYYMM.cpp create mode 100644 dbms/src/Functions/toYYYYMMDD.cpp create mode 100644 dbms/src/Functions/toYYYYMMDDhhmmss.cpp create mode 100644 dbms/src/Functions/toYear.cpp create mode 100644 dbms/src/Functions/today.cpp create mode 100644 dbms/src/Functions/yesterday.cpp diff --git a/dbms/src/Functions/DateTimeTransforms.h b/dbms/src/Functions/DateTimeTransforms.h new file mode 100644 index 00000000000..9be0597698e --- /dev/null +++ b/dbms/src/Functions/DateTimeTransforms.h @@ -0,0 +1,651 @@ +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int ILLEGAL_TYPE_OF_ARGUMENT; + extern const int ILLEGAL_COLUMN; +} + +/** Transformations. + * Represents two functions - from datetime (UInt32) and from date (UInt16). + * + * Also, the "factor transformation" F is defined for the T transformation. + * This is a transformation of F such that its value identifies the region of monotonicity for T + * (for a fixed value of F, the transformation T is monotonic). + * + * Or, figuratively, if T is similar to taking the remainder of division, then F is similar to division. + * + * Example: for transformation T "get the day number in the month" (2015-02-03 -> 3), + * factor-transformation F is "round to the nearest month" (2015-02-03 -> 2015-02-01). + */ + +static inline UInt32 dateIsNotSupported(const char * name) +{ + throw Exception("Illegal type Date of argument for function " + std::string(name), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); +} + +/// This factor transformation will say that the function is monotone everywhere. +struct ZeroTransform +{ + static inline UInt16 execute(UInt32, const DateLUTImpl &) { return 0; } + static inline UInt16 execute(UInt16, const DateLUTImpl &) { return 0; } +}; + +struct ToDateImpl +{ + static constexpr auto name = "toDate"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return UInt16(time_zone.toDayNum(t)); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl &) + { + return d; + } + + using FactorTransform = ZeroTransform; +}; + +struct ToStartOfDayImpl +{ + static constexpr auto name = "toStartOfDay"; + + static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toDate(t); + } + static inline UInt32 execute(UInt16, const DateLUTImpl &) + { + return dateIsNotSupported(name); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToMondayImpl +{ + static constexpr auto name = "toMonday"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toFirstDayNumOfWeek(time_zone.toDayNum(t)); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toFirstDayNumOfWeek(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToStartOfMonthImpl +{ + static constexpr auto name = "toStartOfMonth"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toFirstDayNumOfMonth(time_zone.toDayNum(t)); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toFirstDayNumOfMonth(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToStartOfQuarterImpl +{ + static constexpr auto name = "toStartOfQuarter"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toFirstDayNumOfQuarter(time_zone.toDayNum(t)); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toFirstDayNumOfQuarter(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToStartOfYearImpl +{ + static constexpr auto name = "toStartOfYear"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toFirstDayNumOfYear(time_zone.toDayNum(t)); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toFirstDayNumOfYear(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + + +struct ToTimeImpl +{ + static constexpr auto name = "toTime"; + + /// When transforming to time, the date will be equated to 1970-01-02. + static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toTime(t) + 86400; + } + + static inline UInt32 execute(UInt16, const DateLUTImpl &) + { + return dateIsNotSupported(name); + } + + using FactorTransform = ToDateImpl; +}; + +struct ToStartOfMinuteImpl +{ + static constexpr auto name = "toStartOfMinute"; + + static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toStartOfMinute(t); + } + static inline UInt32 execute(UInt16, const DateLUTImpl &) + { + return dateIsNotSupported(name); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToStartOfFiveMinuteImpl +{ + static constexpr auto name = "toStartOfFiveMinute"; + + static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toStartOfFiveMinute(t); + } + static inline UInt32 execute(UInt16, const DateLUTImpl &) + { + return dateIsNotSupported(name); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToStartOfFifteenMinutesImpl +{ + static constexpr auto name = "toStartOfFifteenMinutes"; + + static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toStartOfFifteenMinutes(t); + } + static inline UInt32 execute(UInt16, const DateLUTImpl &) + { + return dateIsNotSupported(name); + } + + using FactorTransform = ZeroTransform; +}; + +/// Round to start of half-an-hour length interval with unspecified offset. This transform is specific for Yandex.Metrica. +struct TimeSlotImpl +{ + static constexpr auto name = "timeSlot"; + + static inline UInt32 execute(UInt32 t, const DateLUTImpl &) + { + return t / 1800 * 1800; + } + + static inline UInt32 execute(UInt16, const DateLUTImpl &) + { + return dateIsNotSupported(name); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToStartOfHourImpl +{ + static constexpr auto name = "toStartOfHour"; + + static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toStartOfHour(t); + } + + static inline UInt32 execute(UInt16, const DateLUTImpl &) + { + return dateIsNotSupported(name); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToYearImpl +{ + static constexpr auto name = "toYear"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toYear(t); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toYear(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToQuarterImpl +{ + static constexpr auto name = "toQuarter"; + + static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toQuarter(t); + } + static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toQuarter(DayNum(d)); + } + + using FactorTransform = ToStartOfYearImpl; +}; + +struct ToMonthImpl +{ + static constexpr auto name = "toMonth"; + + static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toMonth(t); + } + static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toMonth(DayNum(d)); + } + + using FactorTransform = ToStartOfYearImpl; +}; + +struct ToDayOfMonthImpl +{ + static constexpr auto name = "toDayOfMonth"; + + static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toDayOfMonth(t); + } + static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toDayOfMonth(DayNum(d)); + } + + using FactorTransform = ToStartOfMonthImpl; +}; + +struct ToDayOfWeekImpl +{ + static constexpr auto name = "toDayOfWeek"; + + static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toDayOfWeek(t); + } + static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toDayOfWeek(DayNum(d)); + } + + using FactorTransform = ToMondayImpl; +}; + +struct ToDayOfYearImpl +{ + static constexpr auto name = "toDayOfYear"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toDayOfYear(t); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toDayOfYear(DayNum(d)); + } + + using FactorTransform = ToStartOfYearImpl; +}; + +struct ToHourImpl +{ + static constexpr auto name = "toHour"; + + static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toHour(t); + } + + static inline UInt8 execute(UInt16, const DateLUTImpl &) + { + return dateIsNotSupported(name); + } + + using FactorTransform = ToDateImpl; +}; + +struct ToMinuteImpl +{ + static constexpr auto name = "toMinute"; + + static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toMinute(t); + } + static inline UInt8 execute(UInt16, const DateLUTImpl &) + { + return dateIsNotSupported(name); + } + + using FactorTransform = ToStartOfHourImpl; +}; + +struct ToSecondImpl +{ + static constexpr auto name = "toSecond"; + + static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toSecond(t); + } + static inline UInt8 execute(UInt16, const DateLUTImpl &) + { + return dateIsNotSupported(name); + } + + using FactorTransform = ToStartOfMinuteImpl; +}; + +struct ToISOYearImpl +{ + static constexpr auto name = "toISOYear"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toISOYear(time_zone.toDayNum(t)); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toISOYear(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToStartOfISOYearImpl +{ + static constexpr auto name = "toStartOfISOYear"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toFirstDayNumOfISOYear(time_zone.toDayNum(t)); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toFirstDayNumOfISOYear(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToISOWeekImpl +{ + static constexpr auto name = "toISOWeek"; + + static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toISOWeek(time_zone.toDayNum(t)); + } + static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toISOWeek(DayNum(d)); + } + + using FactorTransform = ToISOYearImpl; +}; + +struct ToRelativeYearNumImpl +{ + static constexpr auto name = "toRelativeYearNum"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toYear(t); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toYear(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToRelativeQuarterNumImpl +{ + static constexpr auto name = "toRelativeQuarterNum"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toRelativeQuarterNum(t); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toRelativeQuarterNum(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToRelativeMonthNumImpl +{ + static constexpr auto name = "toRelativeMonthNum"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toRelativeMonthNum(t); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toRelativeMonthNum(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToRelativeWeekNumImpl +{ + static constexpr auto name = "toRelativeWeekNum"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toRelativeWeekNum(t); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toRelativeWeekNum(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToRelativeDayNumImpl +{ + static constexpr auto name = "toRelativeDayNum"; + + static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toDayNum(t); + } + static inline UInt16 execute(UInt16 d, const DateLUTImpl &) + { + return static_cast(d); + } + + using FactorTransform = ZeroTransform; +}; + + +struct ToRelativeHourNumImpl +{ + static constexpr auto name = "toRelativeHourNum"; + + static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toRelativeHourNum(t); + } + static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toRelativeHourNum(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToRelativeMinuteNumImpl +{ + static constexpr auto name = "toRelativeMinuteNum"; + + static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toRelativeMinuteNum(t); + } + static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toRelativeMinuteNum(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToRelativeSecondNumImpl +{ + static constexpr auto name = "toRelativeSecondNum"; + + static inline UInt32 execute(UInt32 t, const DateLUTImpl &) + { + return t; + } + static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.fromDayNum(DayNum(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToYYYYMMImpl +{ + static constexpr auto name = "toYYYYMM"; + + static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toNumYYYYMM(t); + } + static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toNumYYYYMM(static_cast(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToYYYYMMDDImpl +{ + static constexpr auto name = "toYYYYMMDD"; + + static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toNumYYYYMMDD(t); + } + static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toNumYYYYMMDD(static_cast(d)); + } + + using FactorTransform = ZeroTransform; +}; + +struct ToYYYYMMDDhhmmssImpl +{ + static constexpr auto name = "toYYYYMMDDhhmmss"; + + static inline UInt64 execute(UInt32 t, const DateLUTImpl & time_zone) + { + return time_zone.toNumYYYYMMDDhhmmss(t); + } + static inline UInt64 execute(UInt16 d, const DateLUTImpl & time_zone) + { + return time_zone.toNumYYYYMMDDhhmmss(time_zone.toDate(static_cast(d))); + } + + using FactorTransform = ZeroTransform; +}; + + +template +struct Transformer +{ + static void vector(const PaddedPODArray & vec_from, PaddedPODArray & vec_to, const DateLUTImpl & time_zone) + { + size_t size = vec_from.size(); + vec_to.resize(size); + + for (size_t i = 0; i < size; ++i) + vec_to[i] = Transform::execute(vec_from[i], time_zone); + } +}; + + +template +struct DateTimeTransformImpl +{ + static void execute(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) + { + using Op = Transformer; + + const DateLUTImpl & time_zone = extractTimeZoneFromFunctionArguments(block, arguments, 1, 0); + + const ColumnPtr source_col = block.getByPosition(arguments[0]).column; + if (const auto * sources = checkAndGetColumn>(source_col.get())) + { + auto col_to = ColumnVector::create(); + Op::vector(sources->getData(), col_to->getData(), time_zone); + block.getByPosition(result).column = std::move(col_to); + } + else + { + throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() + + " of first argument of function " + Transform::name, + ErrorCodes::ILLEGAL_COLUMN); + } + } +}; + +} diff --git a/dbms/src/Functions/FunctionDateOrDateTimeAddInterval.h b/dbms/src/Functions/FunctionDateOrDateTimeAddInterval.h new file mode 100644 index 00000000000..c4b7639908f --- /dev/null +++ b/dbms/src/Functions/FunctionDateOrDateTimeAddInterval.h @@ -0,0 +1,308 @@ +#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; +} + + +struct AddSecondsImpl +{ + static constexpr auto name = "addSeconds"; + + static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) + { + return t + delta; + } + + static inline UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) + { + return time_zone.fromDayNum(DayNum(d)) + delta; + } +}; + +struct AddMinutesImpl +{ + static constexpr auto name = "addMinutes"; + + static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) + { + return t + delta * 60; + } + + static inline UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) + { + return time_zone.fromDayNum(DayNum(d)) + delta * 60; + } +}; + +struct AddHoursImpl +{ + static constexpr auto name = "addHours"; + + static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) + { + return t + delta * 3600; + } + + static inline UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) + { + return time_zone.fromDayNum(DayNum(d)) + delta * 3600; + } +}; + +struct AddDaysImpl +{ + static constexpr auto name = "addDays"; + + static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) + { + return time_zone.addDays(t, delta); + } + + static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl &) + { + return d + delta; + } +}; + +struct AddWeeksImpl +{ + static constexpr auto name = "addWeeks"; + + static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) + { + return time_zone.addWeeks(t, delta); + } + + static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl &) + { + return d + delta * 7; + } +}; + +struct AddMonthsImpl +{ + static constexpr auto name = "addMonths"; + + static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) + { + return time_zone.addMonths(t, delta); + } + + static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) + { + return time_zone.addMonths(DayNum(d), delta); + } +}; + +struct AddYearsImpl +{ + static constexpr auto name = "addYears"; + + static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) + { + return time_zone.addYears(t, delta); + } + + static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) + { + return time_zone.addYears(DayNum(d), delta); + } +}; + + +template +struct SubtractIntervalImpl +{ + static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) + { + return Transform::execute(t, -delta, time_zone); + } + + static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) + { + return Transform::execute(d, -delta, time_zone); + } +}; + +struct SubtractSecondsImpl : SubtractIntervalImpl { static constexpr auto name = "subtractSeconds"; }; +struct SubtractMinutesImpl : SubtractIntervalImpl { static constexpr auto name = "subtractMinutes"; }; +struct SubtractHoursImpl : SubtractIntervalImpl { static constexpr auto name = "subtractHours"; }; +struct SubtractDaysImpl : SubtractIntervalImpl { static constexpr auto name = "subtractDays"; }; +struct SubtractWeeksImpl : SubtractIntervalImpl { static constexpr auto name = "subtractWeeks"; }; +struct SubtractMonthsImpl : SubtractIntervalImpl { static constexpr auto name = "subtractMonths"; }; +struct SubtractYearsImpl : SubtractIntervalImpl { static constexpr auto name = "subtractYears"; }; + + +template +struct Adder +{ + static void vector_vector(const PaddedPODArray & vec_from, PaddedPODArray & vec_to, const IColumn & delta, const DateLUTImpl & time_zone) + { + size_t size = vec_from.size(); + vec_to.resize(size); + + for (size_t i = 0; i < size; ++i) + vec_to[i] = Transform::execute(vec_from[i], delta.getInt(i), time_zone); + } + + static void vector_constant(const PaddedPODArray & vec_from, PaddedPODArray & vec_to, Int64 delta, const DateLUTImpl & time_zone) + { + size_t size = vec_from.size(); + vec_to.resize(size); + + for (size_t i = 0; i < size; ++i) + vec_to[i] = Transform::execute(vec_from[i], delta, time_zone); + } + + static void constant_vector(const FromType & from, PaddedPODArray & vec_to, const IColumn & delta, const DateLUTImpl & time_zone) + { + size_t size = delta.size(); + vec_to.resize(size); + + for (size_t i = 0; i < size; ++i) + vec_to[i] = Transform::execute(from, delta.getInt(i), time_zone); + } +}; + + +template +struct DateTimeAddIntervalImpl +{ + static void execute(Block & block, const ColumnNumbers & arguments, size_t result) + { + using ToType = decltype(Transform::execute(FromType(), 0, std::declval())); + using Op = Adder; + + const DateLUTImpl & time_zone = extractTimeZoneFromFunctionArguments(block, arguments, 2, 0); + + const ColumnPtr source_col = block.getByPosition(arguments[0]).column; + + if (const auto * sources = checkAndGetColumn>(source_col.get())) + { + auto col_to = ColumnVector::create(); + + const IColumn & delta_column = *block.getByPosition(arguments[1]).column; + + if (const auto * delta_const_column = typeid_cast(&delta_column)) + Op::vector_constant(sources->getData(), col_to->getData(), delta_const_column->getField().get(), time_zone); + else + Op::vector_vector(sources->getData(), col_to->getData(), delta_column, time_zone); + + block.getByPosition(result).column = std::move(col_to); + } + else if (const auto * sources = checkAndGetColumnConst>(source_col.get())) + { + auto col_to = ColumnVector::create(); + Op::constant_vector(sources->template getValue(), col_to->getData(), *block.getByPosition(arguments[1]).column, time_zone); + block.getByPosition(result).column = std::move(col_to); + } + else + { + throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() + + " of first argument of function " + Transform::name, + ErrorCodes::ILLEGAL_COLUMN); + } + } +}; + + +template +class FunctionDateOrDateTimeAddInterval : public IFunction +{ +public: + static constexpr auto name = Transform::name; + static FunctionPtr create(const Context &) { return std::make_shared(); } + + String getName() const override + { + return name; + } + + 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 (!isNumber(arguments[1].type)) + throw Exception("Second argument for function " + getName() + " (delta) must be number", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + if (arguments.size() == 2) + { + if (!isDateOrDateTime(arguments[0].type)) + throw Exception{"Illegal type " + arguments[0].type->getName() + " of argument of function " + getName() + + ". Should be a date or a date with time", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT}; + } + else + { + if (!WhichDataType(arguments[0].type).isDateTime() + || !WhichDataType(arguments[2].type).isString()) + throw Exception( + "Function " + getName() + " supports 2 or 3 arguments. The 1st argument " + "must be of type Date or DateTime. The 2nd argument must be number. " + "The 3rd argument (optional) must be " + "a constant string with timezone name. The timezone argument is allowed " + "only when the 1st argument has the type DateTime", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + } + + if (WhichDataType(arguments[0].type).isDate()) + { + if (std::is_same_v())), UInt16>) + return std::make_shared(); + else + return std::make_shared(extractTimeZoneNameFromFunctionArguments(arguments, 2, 0)); + } + else + { + if (std::is_same_v())), UInt16>) + return std::make_shared(); + else + return std::make_shared(extractTimeZoneNameFromFunctionArguments(arguments, 2, 0)); + } + } + + bool useDefaultImplementationForConstants() const override { return true; } + ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {2}; } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override + { + const IDataType * from_type = block.getByPosition(arguments[0]).type.get(); + WhichDataType which(from_type); + + if (which.isDate()) + DateTimeAddIntervalImpl::execute(block, arguments, result); + else if (which.isDateTime()) + DateTimeAddIntervalImpl::execute(block, arguments, result); + else + throw Exception("Illegal type " + block.getByPosition(arguments[0]).type->getName() + " of argument of function " + getName(), + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + } +}; + +} + diff --git a/dbms/src/Functions/FunctionDateOrDateTimeToSomething.h b/dbms/src/Functions/FunctionDateOrDateTimeToSomething.h new file mode 100644 index 00000000000..b5345f197ee --- /dev/null +++ b/dbms/src/Functions/FunctionDateOrDateTimeToSomething.h @@ -0,0 +1,125 @@ +#include +#include + +#include +#include + +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int ILLEGAL_TYPE_OF_ARGUMENT; + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; +} + + +/// See DateTimeTransforms.h +template +class FunctionDateOrDateTimeToSomething : public IFunction +{ +public: + static constexpr auto name = Transform::name; + static FunctionPtr create(const Context &) { return std::make_shared(); } + + String getName() const override + { + return name; + } + + bool isVariadic() const override { return true; } + size_t getNumberOfArguments() const override { return 0; } + + DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override + { + if (arguments.size() == 1) + { + if (!isDateOrDateTime(arguments[0].type)) + throw Exception("Illegal type " + arguments[0].type->getName() + " of argument of function " + getName() + + ". Should be a date or a date with time", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + } + else if (arguments.size() == 2) + { + if (!WhichDataType(arguments[0].type).isDateTime() + || !WhichDataType(arguments[1].type).isString()) + throw Exception( + "Function " + getName() + " supports 1 or 2 arguments. The 1st argument " + "must be of type Date or DateTime. The 2nd argument (optional) must be " + "a constant string with timezone name. The timezone argument is allowed " + "only when the 1st argument has the type DateTime", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + } + else + throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + + toString(arguments.size()) + ", should be 1 or 2", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + /// For DateTime, if time zone is specified, attach it to type. + if (std::is_same_v) + return std::make_shared(extractTimeZoneNameFromFunctionArguments(arguments, 1, 0)); + else + return std::make_shared(); + } + + bool useDefaultImplementationForConstants() const override { return true; } + ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1}; } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + const IDataType * from_type = block.getByPosition(arguments[0]).type.get(); + WhichDataType which(from_type); + + if (which.isDate()) + DateTimeTransformImpl::execute(block, arguments, result, input_rows_count); + else if (which.isDateTime()) + DateTimeTransformImpl::execute(block, arguments, result, input_rows_count); + else + throw Exception("Illegal type " + block.getByPosition(arguments[0]).type->getName() + " of argument of function " + getName(), + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + } + + + bool hasInformationAboutMonotonicity() const override + { + return true; + } + + Monotonicity getMonotonicityForRange(const IDataType & type, const Field & left, const Field & right) const override + { + IFunction::Monotonicity is_monotonic { true }; + IFunction::Monotonicity is_not_monotonic; + + if (std::is_same_v) + { + is_monotonic.is_always_monotonic = true; + return is_monotonic; + } + + /// This method is called only if the function has one argument. Therefore, we do not care about the non-local time zone. + const DateLUTImpl & date_lut = DateLUT::instance(); + + if (left.isNull() || right.isNull()) + return is_not_monotonic; + + /// The function is monotonous on the [left, right] segment, if the factor transformation returns the same values for them. + + if (checkAndGetDataType(&type)) + { + return Transform::FactorTransform::execute(UInt16(left.get()), date_lut) + == Transform::FactorTransform::execute(UInt16(right.get()), date_lut) + ? is_monotonic : is_not_monotonic; + } + else + { + return Transform::FactorTransform::execute(UInt32(left.get()), date_lut) + == Transform::FactorTransform::execute(UInt32(right.get()), date_lut) + ? is_monotonic : is_not_monotonic; + } + } +}; + +} + diff --git a/dbms/src/Functions/FunctionsConversion.h b/dbms/src/Functions/FunctionsConversion.h index 283da2a99e7..c7e5843212c 100644 --- a/dbms/src/Functions/FunctionsConversion.h +++ b/dbms/src/Functions/FunctionsConversion.h @@ -35,8 +35,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/dbms/src/Functions/addDays.cpp b/dbms/src/Functions/addDays.cpp new file mode 100644 index 00000000000..84849974494 --- /dev/null +++ b/dbms/src/Functions/addDays.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionAddDays = FunctionDateOrDateTimeAddInterval; + +void registerFunctionAddDays(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/addHours.cpp b/dbms/src/Functions/addHours.cpp new file mode 100644 index 00000000000..0ed644c89d7 --- /dev/null +++ b/dbms/src/Functions/addHours.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionAddHours = FunctionDateOrDateTimeAddInterval; + +void registerFunctionAddHours(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/addMinutes.cpp b/dbms/src/Functions/addMinutes.cpp new file mode 100644 index 00000000000..ba7b78f1a98 --- /dev/null +++ b/dbms/src/Functions/addMinutes.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionAddMinutes = FunctionDateOrDateTimeAddInterval; + +void registerFunctionAddMinutes(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/addMonths.cpp b/dbms/src/Functions/addMonths.cpp new file mode 100644 index 00000000000..0f8d97081d2 --- /dev/null +++ b/dbms/src/Functions/addMonths.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionAddMonths = FunctionDateOrDateTimeAddInterval; + +void registerFunctionAddMonths(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/addSeconds.cpp b/dbms/src/Functions/addSeconds.cpp new file mode 100644 index 00000000000..1602d01de85 --- /dev/null +++ b/dbms/src/Functions/addSeconds.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionAddSeconds = FunctionDateOrDateTimeAddInterval; + +void registerFunctionAddSeconds(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/addWeeks.cpp b/dbms/src/Functions/addWeeks.cpp new file mode 100644 index 00000000000..b87c40bd813 --- /dev/null +++ b/dbms/src/Functions/addWeeks.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionAddWeeks = FunctionDateOrDateTimeAddInterval; + +void registerFunctionAddWeeks(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/addYears.cpp b/dbms/src/Functions/addYears.cpp new file mode 100644 index 00000000000..044860abb22 --- /dev/null +++ b/dbms/src/Functions/addYears.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionAddYears = FunctionDateOrDateTimeAddInterval; + +void registerFunctionAddYears(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/dateDiff.cpp b/dbms/src/Functions/dateDiff.cpp new file mode 100644 index 00000000000..859896ad863 --- /dev/null +++ b/dbms/src/Functions/dateDiff.cpp @@ -0,0 +1,220 @@ +#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; +} + +/** dateDiff('unit', t1, t2, [timezone]) + * t1 and t2 can be Date or DateTime + * + * If timezone is specified, it applied to both arguments. + * If not, timezones from datatypes t1 and t2 are used. + * If that timezones are not the same, the result is unspecified. + * + * Timezone matters because days can have different length. + */ +class FunctionDateDiff : public IFunction +{ +public: + static constexpr auto name = "dateDiff"; + static FunctionPtr create(const Context &) { return std::make_shared(); } + + String getName() const override + { + return name; + } + + bool isVariadic() const override { return true; } + size_t getNumberOfArguments() const override { return 0; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (arguments.size() != 3 && arguments.size() != 4) + throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + + toString(arguments.size()) + ", should be 3 or 4", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + if (!isString(arguments[0])) + throw Exception("First argument for function " + getName() + " (unit) must be String", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + if (!isDateOrDateTime(arguments[1])) + throw Exception("Second argument for function " + getName() + " must be Date or DateTime", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + if (!isDateOrDateTime(arguments[2])) + throw Exception("Third argument for function " + getName() + " must be Date or DateTime", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + if (arguments.size() == 4 && !isString(arguments[3])) + throw Exception("Fourth argument for function " + getName() + " (timezone) must be String", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + return std::make_shared(); + } + + bool useDefaultImplementationForConstants() const override { return true; } + ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {0, 3}; } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + auto * unit_column = checkAndGetColumnConst(block.getByPosition(arguments[0]).column.get()); + if (!unit_column) + throw Exception("First argument for function " + getName() + " must be constant String", ErrorCodes::ILLEGAL_COLUMN); + + String unit = Poco::toLower(unit_column->getValue()); + + const IColumn & x = *block.getByPosition(arguments[1]).column; + const IColumn & y = *block.getByPosition(arguments[2]).column; + + size_t rows = input_rows_count; + auto res = ColumnInt64::create(rows); + + const DateLUTImpl & timezone_x = extractTimeZoneFromFunctionArguments(block, arguments, 3, 1); + const DateLUTImpl & timezone_y = extractTimeZoneFromFunctionArguments(block, arguments, 3, 2); + + if (unit == "year" || unit == "yy" || unit == "yyyy") + dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); + else if (unit == "quarter" || unit == "qq" || unit == "q") + dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); + else if (unit == "month" || unit == "mm" || unit == "m") + dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); + else if (unit == "week" || unit == "wk" || unit == "ww") + dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); + else if (unit == "day" || unit == "dd" || unit == "d") + dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); + else if (unit == "hour" || unit == "hh") + dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); + else if (unit == "minute" || unit == "mi" || unit == "n") + dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); + else if (unit == "second" || unit == "ss" || unit == "s") + dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); + else + throw Exception("Function " + getName() + " does not support '" + unit + "' unit", ErrorCodes::BAD_ARGUMENTS); + + block.getByPosition(result).column = std::move(res); + } + +private: + template + void dispatchForColumns( + const IColumn & x, const IColumn & y, + const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, + ColumnInt64::Container & result) + { + if (auto * x_vec = checkAndGetColumn(&x)) + dispatchForSecondColumn(*x_vec, y, timezone_x, timezone_y, result); + else if (auto * x_vec = checkAndGetColumn(&x)) + dispatchForSecondColumn(*x_vec, y, timezone_x, timezone_y, result); + else if (auto * x_const = checkAndGetColumnConst(&x)) + dispatchConstForSecondColumn(x_const->getValue(), y, timezone_x, timezone_y, result); + else if (auto * x_const = checkAndGetColumnConst(&x)) + dispatchConstForSecondColumn(x_const->getValue(), y, timezone_x, timezone_y, result); + else + throw Exception("Illegal column for first argument of function " + getName() + ", must be Date or DateTime", ErrorCodes::ILLEGAL_COLUMN); + } + + template + void dispatchForSecondColumn( + const ColumnVector & x, const IColumn & y, + const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, + ColumnInt64::Container & result) + { + if (auto * y_vec = checkAndGetColumn(&y)) + vector_vector(x, *y_vec, timezone_x, timezone_y, result); + else if (auto * y_vec = checkAndGetColumn(&y)) + vector_vector(x, *y_vec, timezone_x, timezone_y, result); + else if (auto * y_const = checkAndGetColumnConst(&y)) + vector_constant(x, y_const->getValue(), timezone_x, timezone_y, result); + else if (auto * y_const = checkAndGetColumnConst(&y)) + vector_constant(x, y_const->getValue(), timezone_x, timezone_y, result); + else + throw Exception("Illegal column for second argument of function " + getName() + ", must be Date or DateTime", ErrorCodes::ILLEGAL_COLUMN); + } + + template + void dispatchConstForSecondColumn( + T1 x, const IColumn & y, + const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, + ColumnInt64::Container & result) + { + if (auto * y_vec = checkAndGetColumn(&y)) + constant_vector(x, *y_vec, timezone_x, timezone_y, result); + else if (auto * y_vec = checkAndGetColumn(&y)) + constant_vector(x, *y_vec, timezone_x, timezone_y, result); + else + throw Exception("Illegal column for second argument of function " + getName() + ", must be Date or DateTime", ErrorCodes::ILLEGAL_COLUMN); + } + + template + void vector_vector( + const ColumnVector & x, const ColumnVector & y, + const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, + ColumnInt64::Container & result) + { + const auto & x_data = x.getData(); + const auto & y_data = y.getData(); + for (size_t i = 0, size = x.size(); i < size; ++i) + result[i] = calculate(x_data[i], y_data[i], timezone_x, timezone_y); + } + + template + void vector_constant( + const ColumnVector & x, T2 y, + const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, + ColumnInt64::Container & result) + { + const auto & x_data = x.getData(); + for (size_t i = 0, size = x.size(); i < size; ++i) + result[i] = calculate(x_data[i], y, timezone_x, timezone_y); + } + + template + void constant_vector( + T1 x, const ColumnVector & y, + const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, + ColumnInt64::Container & result) + { + const auto & y_data = y.getData(); + for (size_t i = 0, size = y.size(); i < size; ++i) + result[i] = calculate(x, y_data[i], timezone_x, timezone_y); + } + + template + Int64 calculate(T1 x, T2 y, const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y) + { + return Int64(Transform::execute(y, timezone_y)) + - Int64(Transform::execute(x, timezone_x)); + } +}; + +void registerFunctionDateDiff(FunctionFactory & factory) +{ + factory.registerFunction(FunctionFactory::CaseInsensitive); +} + +} + diff --git a/dbms/src/Functions/extractTimeZoneFromFunctionArguments.cpp b/dbms/src/Functions/extractTimeZoneFromFunctionArguments.cpp new file mode 100644 index 00000000000..4190cfe797f --- /dev/null +++ b/dbms/src/Functions/extractTimeZoneFromFunctionArguments.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int ILLEGAL_COLUMN; +} + + +static std::string extractTimeZoneNameFromColumn(const IColumn & column) +{ + const ColumnConst * time_zone_column = checkAndGetColumnConst(&column); + + if (!time_zone_column) + throw Exception("Illegal column " + column.getName() + + " of time zone argument of function, must be constant string", + ErrorCodes::ILLEGAL_COLUMN); + + return time_zone_column->getValue(); +} + + +std::string extractTimeZoneNameFromFunctionArguments(const ColumnsWithTypeAndName & arguments, size_t time_zone_arg_num, size_t datetime_arg_num) +{ + /// Explicit time zone may be passed in last argument. + if (arguments.size() == time_zone_arg_num + 1 && arguments[time_zone_arg_num].column) + { + return extractTimeZoneNameFromColumn(*arguments[time_zone_arg_num].column); + } + else + { + if (!arguments.size()) + return {}; + + /// If time zone is attached to an argument of type DateTime. + if (const DataTypeDateTime * type = checkAndGetDataType(arguments[datetime_arg_num].type.get())) + return type->getTimeZone().getTimeZone(); + + return {}; + } +} + +const DateLUTImpl & extractTimeZoneFromFunctionArguments(Block & block, const ColumnNumbers & arguments, size_t time_zone_arg_num, size_t datetime_arg_num) +{ + if (arguments.size() == time_zone_arg_num + 1) + return DateLUT::instance(extractTimeZoneNameFromColumn(*block.getByPosition(arguments[time_zone_arg_num]).column)); + else + { + if (!arguments.size()) + return DateLUT::instance(); + + /// If time zone is attached to an argument of type DateTime. + if (const DataTypeDateTime * type = checkAndGetDataType(block.getByPosition(arguments[datetime_arg_num]).type.get())) + return type->getTimeZone(); + + return DateLUT::instance(); + } +} + +} + diff --git a/dbms/src/Functions/extractTimeZoneFromFunctionArguments.h b/dbms/src/Functions/extractTimeZoneFromFunctionArguments.h new file mode 100644 index 00000000000..7c90feb7da2 --- /dev/null +++ b/dbms/src/Functions/extractTimeZoneFromFunctionArguments.h @@ -0,0 +1,17 @@ +#include +#include +#include + + +class DateLUTImpl; + +namespace DB +{ + +class Block; + +/// Determine working timezone either from optional argument with time zone name or from time zone in DateTime type of argument. +std::string extractTimeZoneNameFromFunctionArguments(const ColumnsWithTypeAndName & arguments, size_t time_zone_arg_num, size_t datetime_arg_num); +const DateLUTImpl & extractTimeZoneFromFunctionArguments(Block & block, const ColumnNumbers & arguments, size_t time_zone_arg_num, size_t datetime_arg_num); + +} diff --git a/dbms/src/Functions/formatDateTime.cpp b/dbms/src/Functions/formatDateTime.cpp new file mode 100644 index 00000000000..874cfcfcd5e --- /dev/null +++ b/dbms/src/Functions/formatDateTime.cpp @@ -0,0 +1,571 @@ +#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 NOT_IMPLEMENTED; + extern const int ILLEGAL_COLUMN; + extern const int BAD_ARGUMENTS; +} + + +/** formatDateTime(time, 'pattern') + * Performs formatting of time, according to provided pattern. + * + * This function is optimized with an assumption, that the resulting strings are fixed width. + * (This assumption is fulfilled for currently supported formatting options). + * + * It is implemented in two steps. + * At first step, it creates a pattern of zeros, literal characters, whitespaces, etc. + * and quickly fills resulting charater array (string column) with this pattern. + * At second step, it walks across the resulting character array and modifies/replaces specific charaters, + * by calling some functions by pointers and shifting cursor by specified amount. + * + * Advantages: + * - memcpy is mostly unrolled; + * - low number of arithmetic ops due to pre-filled pattern; + * - for somewhat reason, function by pointer call is faster than switch/case. + * + * Possible further optimization options: + * - slightly interleave first and second step for better cache locality + * (but it has no sense when character array fits in L1d cache); + * - avoid indirect function calls and inline functions with JIT compilation. + * + * Performance on Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz: + * + * WITH formatDateTime(now() + number, '%H:%M:%S') AS x SELECT count() FROM system.numbers WHERE NOT ignore(x); + * - 97 million rows per second per core; + * + * WITH formatDateTime(toDateTime('2018-01-01 00:00:00') + number, '%F %T') AS x SELECT count() FROM system.numbers WHERE NOT ignore(x) + * - 71 million rows per second per core; + * + * select count() from (select formatDateTime(t, '%m/%d/%Y %H:%M:%S') from (select toDateTime('2018-01-01 00:00:00')+number as t from numbers(100000000))); + * - 53 million rows per second per core; + * + * select count() from (select formatDateTime(t, 'Hello %Y World') from (select toDateTime('2018-01-01 00:00:00')+number as t from numbers(100000000))); + * - 138 million rows per second per core; + * + * PS. We can make this function to return FixedString. Currently it returns String. + */ +class FunctionFormatDateTime : public IFunction +{ +private: + /// Time is either UInt32 for DateTime or UInt16 for Date. + template + class Action + { + public: + using Func = void (*)(char *, Time, const DateLUTImpl &); + + Func func; + size_t shift; + + Action(Func func, size_t shift = 0) : func(func), shift(shift) {} + + void perform(char *& target, Time source, const DateLUTImpl & timezone) + { + func(target, source, timezone); + target += shift; + } + + private: + template + static inline void writeNumber2(char * p, T v) + { + static const char digits[201] = + "00010203040506070809" + "10111213141516171819" + "20212223242526272829" + "30313233343536373839" + "40414243444546474849" + "50515253545556575859" + "60616263646566676869" + "70717273747576777879" + "80818283848586878889" + "90919293949596979899"; + + memcpy(p, &digits[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); + } + + public: + static void noop(char *, Time, const DateLUTImpl &) + { + } + + static void century(char * target, Time source, const DateLUTImpl & timezone) + { + auto year = ToYearImpl::execute(source, timezone); + auto century = year / 100; + writeNumber2(target, century); + } + + static void dayOfMonth(char * target, Time source, const DateLUTImpl & timezone) + { + writeNumber2(target, ToDayOfMonthImpl::execute(source, timezone)); + } + + static void americanDate(char * target, Time source, const DateLUTImpl & timezone) + { + writeNumber2(target, ToMonthImpl::execute(source, timezone)); + writeNumber2(target + 3, ToDayOfMonthImpl::execute(source, timezone)); + writeNumber2(target + 6, ToYearImpl::execute(source, timezone) % 100); + } + + static void dayOfMonthSpacePadded(char * target, Time source, const DateLUTImpl & timezone) + { + auto day = ToDayOfMonthImpl::execute(source, timezone); + if (day < 10) + target[1] += day; + else + writeNumber2(target, day); + } + + static void ISO8601Date(char * target, Time source, const DateLUTImpl & timezone) + { + writeNumber4(target, ToYearImpl::execute(source, timezone)); + writeNumber2(target + 5, ToMonthImpl::execute(source, timezone)); + writeNumber2(target + 8, ToDayOfMonthImpl::execute(source, timezone)); + } + + static void dayOfYear(char * target, Time source, const DateLUTImpl & timezone) + { + writeNumber3(target, ToDayOfYearImpl::execute(source, timezone)); + } + + static void month(char * target, Time source, const DateLUTImpl & timezone) + { + writeNumber2(target, ToMonthImpl::execute(source, timezone)); + } + + static void dayOfWeek(char * target, Time source, const DateLUTImpl & timezone) + { + *target += ToDayOfWeekImpl::execute(source, timezone); + } + + static void dayOfWeek0To6(char * target, Time source, const DateLUTImpl & timezone) + { + auto day = ToDayOfWeekImpl::execute(source, timezone); + *target += (day == 7 ? 0 : day); + } + + static void ISO8601Week(char * target, Time source, const DateLUTImpl & timezone) + { + writeNumber2(target, ToISOWeekImpl::execute(source, timezone)); + } + + static void year2(char * target, Time source, const DateLUTImpl & timezone) + { + writeNumber2(target, ToYearImpl::execute(source, timezone) % 100); + } + + static void year4(char * target, Time source, const DateLUTImpl & timezone) + { + writeNumber4(target, ToYearImpl::execute(source, timezone)); + } + + static void hour24(char * target, Time source, const DateLUTImpl & timezone) + { + writeNumber2(target, ToHourImpl::execute(source, timezone)); + } + + static void hour12(char * target, Time source, const DateLUTImpl & timezone) + { + auto x = ToHourImpl::execute(source, timezone); + writeNumber2(target, x == 0 ? 12 : (x > 12 ? x - 12 : x)); + } + + static void minute(char * target, Time source, const DateLUTImpl & timezone) + { + writeNumber2(target, ToMinuteImpl::execute(source, timezone)); + } + + static void AMPM(char * target, Time source, const DateLUTImpl & timezone) + { + auto hour = ToHourImpl::execute(source, timezone); + if (hour >= 12) + *target = 'P'; + } + + static void hhmm24(char * target, Time source, const DateLUTImpl & timezone) + { + writeNumber2(target, ToHourImpl::execute(source, timezone)); + writeNumber2(target + 3, ToMinuteImpl::execute(source, timezone)); + } + + static void second(char * target, Time source, const DateLUTImpl & timezone) + { + writeNumber2(target, ToSecondImpl::execute(source, timezone)); + } + + static void ISO8601Time(char * target, Time source, const DateLUTImpl & timezone) + { + writeNumber2(target, ToHourImpl::execute(source, timezone)); + writeNumber2(target + 3, ToMinuteImpl::execute(source, timezone)); + writeNumber2(target + 6, ToSecondImpl::execute(source, timezone)); + } + }; + +public: + static constexpr auto name = "formatDateTime"; + + static FunctionPtr create(const Context &) { return std::make_shared(); } + + String getName() const override + { + return name; + } + + bool useDefaultImplementationForConstants() const override { return true; } + + ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1, 2}; } + + 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).isDateOrDateTime()) + throw Exception("Illegal type " + arguments[0].type->getName() + " of 1 argument of function " + getName() + + ". Should be a date or a date with time", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + if (!WhichDataType(arguments[1].type).isString()) + throw Exception("Illegal type " + arguments[1].type->getName() + " of 2 argument of function " + getName() + ". Must be String.", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + if (arguments.size() == 3) + { + if (!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(); + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override + { + if (!executeType(block, arguments, result) + && !executeType(block, arguments, result)) + throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() + + " of function " + getName() + ", must be Date or DateTime", + ErrorCodes::ILLEGAL_COLUMN); + } + + template + bool executeType(Block & block, const ColumnNumbers & arguments, size_t result) + { + if (auto * times = checkAndGetColumn>(block.getByPosition(arguments[0]).column.get())) + { + const ColumnConst * pattern_column = checkAndGetColumnConst(block.getByPosition(arguments[1]).column.get()); + + if (!pattern_column) + throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName() + + " of second ('format') argument of function " + getName() + + ". Must be constant string.", + ErrorCodes::ILLEGAL_COLUMN); + + String pattern = pattern_column->getValue(); + + std::vector> instructions; + String pattern_to_fill = parsePattern(pattern, instructions); + size_t result_size = pattern_to_fill.size(); + + const DateLUTImpl * time_zone_tmp = nullptr; + if (arguments.size() == 3) + time_zone_tmp = &extractTimeZoneFromFunctionArguments(block, arguments, 2, 0); + else + time_zone_tmp = &DateLUT::instance(); + + const DateLUTImpl & time_zone = *time_zone_tmp; + + const typename ColumnVector::Container & vec = times->getData(); + + auto col_res = ColumnString::create(); + auto & dst_data = col_res->getChars(); + auto & dst_offsets = col_res->getOffsets(); + dst_data.resize(vec.size() * (result_size + 1)); + dst_offsets.resize(vec.size()); + + /// Fill result with literals. + { + UInt8 * begin = dst_data.data(); + UInt8 * end = begin + dst_data.size(); + UInt8 * pos = begin; + + if (pos < end) + { + memcpy(pos, pattern_to_fill.data(), result_size + 1); /// With zero terminator. + pos += result_size + 1; + } + + /// Fill by copying exponential growing ranges. + while (pos < end) + { + size_t bytes_to_copy = std::min(pos - begin, end - pos); + memcpy(pos, begin, bytes_to_copy); + pos += bytes_to_copy; + } + } + + auto begin = reinterpret_cast(dst_data.data()); + auto pos = begin; + + for (size_t i = 0; i < vec.size(); ++i) + { + for(auto & instruction : instructions) + instruction.perform(pos, vec[i], time_zone); + + dst_offsets[i] = pos - begin; + } + + dst_data.resize(pos - begin); + block.getByPosition(result).column = std::move(col_res); + return true; + } + + return false; + } + + template + String parsePattern(const String & pattern, std::vector> & instructions) const + { + String result; + + const char * pos = pattern.data(); + const char * end = pos + pattern.size(); + + /// Add shift to previous action; or if there were none, add noop action with shift. + auto addShift = [&](size_t amount) + { + if (instructions.empty()) + instructions.emplace_back(&Action::noop); + instructions.back().shift += amount; + }; + + /// If the argument was DateTime, add instruction for printing. If it was date, just shift (the buffer is pre-filled with default values). + auto addInstructionOrShift = [&](typename Action::Func func [[maybe_unused]], size_t shift) + { + if constexpr (std::is_same_v) + instructions.emplace_back(func, shift); + else + addShift(shift); + }; + + while (true) + { + const char * percent_pos = find_first_symbols<'%'>(pos, end); + + if (percent_pos < end) + { + if (pos < percent_pos) + { + result.append(pos, percent_pos); + addShift(percent_pos - pos); + } + + pos = percent_pos + 1; + + if (pos >= end) + throw Exception("Sign '%' is the last in pattern, if you need it, use '%%'", ErrorCodes::BAD_ARGUMENTS); + + switch (*pos) + { + // Year, divided by 100, zero-padded + case 'C': + instructions.emplace_back(&Action::century, 2); + result.append("00"); + break; + + // Day of month, zero-padded (01-31) + case 'd': + instructions.emplace_back(&Action::dayOfMonth, 2); + result.append("00"); + break; + + // Short MM/DD/YY date, equivalent to %m/%d/%y + case 'D': + instructions.emplace_back(&Action::americanDate, 8); + result.append("00/00/00"); + break; + + // Day of month, space-padded ( 1-31) 23 + case 'e': + instructions.emplace_back(&Action::dayOfMonthSpacePadded, 2); + result.append(" 0"); + break; + + // Short YYYY-MM-DD date, equivalent to %Y-%m-%d 2001-08-23 + case 'F': + instructions.emplace_back(&Action::ISO8601Date, 10); + result.append("0000-00-00"); + break; + + // Day of the year (001-366) 235 + case 'j': + instructions.emplace_back(&Action::dayOfYear, 3); + result.append("000"); + break; + + // Month as a decimal number (01-12) + case 'm': + instructions.emplace_back(&Action::month, 2); + result.append("00"); + break; + + // ISO 8601 weekday as number with Monday as 1 (1-7) + case 'u': + instructions.emplace_back(&Action::dayOfWeek, 1); + result.append("0"); + break; + + // ISO 8601 week number (01-53) + case 'V': + instructions.emplace_back(&Action::ISO8601Week, 2); + result.append("00"); + break; + + // Weekday as a decimal number with Sunday as 0 (0-6) 4 + case 'w': + instructions.emplace_back(&Action::dayOfWeek0To6, 1); + result.append("0"); + break; + + // Two digits year + case 'y': + instructions.emplace_back(&Action::year2, 2); + result.append("00"); + break; + + // Four digits year + case 'Y': + instructions.emplace_back(&Action::year4, 4); + result.append("0000"); + break; + + /// Time components. If the argument is Date, not a DateTime, then this components will have default value. + + // Minute (00-59) + case 'M': + addInstructionOrShift(&Action::minute, 2); + result.append("00"); + break; + + // AM or PM + case 'p': + addInstructionOrShift(&Action::AMPM, 2); + result.append("AM"); + break; + + // 24-hour HH:MM time, equivalent to %H:%M 14:55 + case 'R': + addInstructionOrShift(&Action::hhmm24, 5); + result.append("00:00"); + break; + + // Seconds + case 'S': + addInstructionOrShift(&Action::second, 2); + result.append("00"); + break; + + // ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S 14:55:02 + case 'T': + addInstructionOrShift(&Action::ISO8601Time, 8); + result.append("00:00:00"); + break; + + // Hour in 24h format (00-23) + case 'H': + addInstructionOrShift(&Action::hour24, 2); + result.append("00"); + break; + + // Hour in 12h format (01-12) + case 'I': + addInstructionOrShift(&Action::hour12, 2); + result.append("12"); + break; + + /// Escaped literal characters. + case '%': + result += '%'; + addShift(1); + break; + case 't': + result += '\t'; + addShift(1); + break; + case 'n': + result += '\n'; + addShift(1); + break; + + // Unimplemented + case 'U': [[fallthrough]]; + case 'W': + throw Exception("Wrong pattern '" + pattern + "', symbol '" + *pos + " is not implemented ' for function " + getName(), + ErrorCodes::NOT_IMPLEMENTED); + + default: + throw Exception( + "Wrong pattern '" + pattern + "', unexpected symbol '" + *pos + "' for function " + getName(), ErrorCodes::ILLEGAL_COLUMN); + } + + ++pos; + } + else + { + result.append(pos, end); + addShift(end + 1 - pos); /// including zero terminator + break; + } + } + + return result; + } +}; + +void registerFunctionFormatDateTime(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} diff --git a/dbms/src/Functions/now.cpp b/dbms/src/Functions/now.cpp new file mode 100644 index 00000000000..59501d96088 --- /dev/null +++ b/dbms/src/Functions/now.cpp @@ -0,0 +1,44 @@ +#include + +#include +#include + + +namespace DB +{ + +/// Get the current time. (It is a constant, it is evaluated once for the entire query.) +class FunctionNow : public IFunction +{ +public: + static constexpr auto name = "now"; + static FunctionPtr create(const Context &) { return std::make_shared(); } + + String getName() const override + { + return name; + } + + size_t getNumberOfArguments() const override { return 0; } + + DataTypePtr getReturnTypeImpl(const DataTypes & /*arguments*/) const override + { + return std::make_shared(); + } + + bool isDeterministic() const override { return false; } + + void executeImpl(Block & block, const ColumnNumbers &, size_t result, size_t input_rows_count) override + { + block.getByPosition(result).column = DataTypeDateTime().createColumnConst( + input_rows_count, + static_cast(time(nullptr))); + } +}; + +void registerFunctionNow(FunctionFactory & factory) +{ + factory.registerFunction(FunctionFactory::CaseInsensitive); +} + +} diff --git a/dbms/src/Functions/subtractDays.cpp b/dbms/src/Functions/subtractDays.cpp new file mode 100644 index 00000000000..21966a15311 --- /dev/null +++ b/dbms/src/Functions/subtractDays.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionSubtractDays = FunctionDateOrDateTimeAddInterval; + +void registerFunctionSubtractDays(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/subtractHours.cpp b/dbms/src/Functions/subtractHours.cpp new file mode 100644 index 00000000000..e71c9d74a01 --- /dev/null +++ b/dbms/src/Functions/subtractHours.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionSubtractHours = FunctionDateOrDateTimeAddInterval; + +void registerFunctionSubtractHours(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/subtractMinutes.cpp b/dbms/src/Functions/subtractMinutes.cpp new file mode 100644 index 00000000000..ba9d593a64d --- /dev/null +++ b/dbms/src/Functions/subtractMinutes.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionSubtractMinutes = FunctionDateOrDateTimeAddInterval; + +void registerFunctionSubtractMinutes(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/subtractMonths.cpp b/dbms/src/Functions/subtractMonths.cpp new file mode 100644 index 00000000000..64eeba2ce86 --- /dev/null +++ b/dbms/src/Functions/subtractMonths.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionSubtractMonths = FunctionDateOrDateTimeAddInterval; + +void registerFunctionSubtractMonths(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/subtractSeconds.cpp b/dbms/src/Functions/subtractSeconds.cpp new file mode 100644 index 00000000000..81a7f7e2df1 --- /dev/null +++ b/dbms/src/Functions/subtractSeconds.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionSubtractSeconds = FunctionDateOrDateTimeAddInterval; + +void registerFunctionSubtractSeconds(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/subtractWeeks.cpp b/dbms/src/Functions/subtractWeeks.cpp new file mode 100644 index 00000000000..55b52043dd0 --- /dev/null +++ b/dbms/src/Functions/subtractWeeks.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionSubtractWeeks = FunctionDateOrDateTimeAddInterval; + +void registerFunctionSubtractWeeks(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/subtractYears.cpp b/dbms/src/Functions/subtractYears.cpp new file mode 100644 index 00000000000..241142722d5 --- /dev/null +++ b/dbms/src/Functions/subtractYears.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + + +namespace DB +{ + +using FunctionSubtractYears = FunctionDateOrDateTimeAddInterval; + +void registerFunctionSubtractYears(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/timeSlot.cpp b/dbms/src/Functions/timeSlot.cpp new file mode 100644 index 00000000000..f9763997b92 --- /dev/null +++ b/dbms/src/Functions/timeSlot.cpp @@ -0,0 +1,21 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionTimeSlot = FunctionDateOrDateTimeToSomething; + +void registerFunctionTimeSlot(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + + diff --git a/dbms/src/Functions/timeSlots.cpp b/dbms/src/Functions/timeSlots.cpp new file mode 100644 index 00000000000..16ade60cb50 --- /dev/null +++ b/dbms/src/Functions/timeSlots.cpp @@ -0,0 +1,182 @@ +#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; +} + +/** timeSlots(StartTime, Duration) + * - for the time interval beginning at `StartTime` and continuing `Duration` seconds, + * returns an array of time points, consisting of rounding down to half an hour of points from this interval. + * For example, timeSlots(toDateTime('2012-01-01 12:20:00'), 600) = [toDateTime('2012-01-01 12:00:00'), toDateTime('2012-01-01 12:30:00')]. + * This is necessary to search for hits that are part of the corresponding visit. + * + * This is obsolete function. It was developed for Yandex.Metrica, but no longer used. + */ + +template +struct TimeSlotsImpl +{ + static constexpr UInt32 TIME_SLOT_SIZE = 1800; + + static void vector_vector( + const PaddedPODArray & starts, const PaddedPODArray & durations, + PaddedPODArray & result_values, ColumnArray::Offsets & result_offsets) + { + size_t size = starts.size(); + + result_offsets.resize(size); + result_values.reserve(size); + + ColumnArray::Offset current_offset = 0; + for (size_t i = 0; i < size; ++i) + { + for (UInt32 value = starts[i] / TIME_SLOT_SIZE; value <= (starts[i] + durations[i]) / TIME_SLOT_SIZE; ++value) + { + result_values.push_back(value * TIME_SLOT_SIZE); + ++current_offset; + } + + result_offsets[i] = current_offset; + } + } + + static void vector_constant( + const PaddedPODArray & starts, DurationType duration, + PaddedPODArray & result_values, ColumnArray::Offsets & result_offsets) + { + size_t size = starts.size(); + + result_offsets.resize(size); + result_values.reserve(size); + + ColumnArray::Offset current_offset = 0; + for (size_t i = 0; i < size; ++i) + { + for (UInt32 value = starts[i] / TIME_SLOT_SIZE; value <= (starts[i] + duration) / TIME_SLOT_SIZE; ++value) + { + result_values.push_back(value * TIME_SLOT_SIZE); + ++current_offset; + } + + result_offsets[i] = current_offset; + } + } + + static void constant_vector( + UInt32 start, const PaddedPODArray & durations, + PaddedPODArray & result_values, ColumnArray::Offsets & result_offsets) + { + size_t size = durations.size(); + + result_offsets.resize(size); + result_values.reserve(size); + + ColumnArray::Offset current_offset = 0; + for (size_t i = 0; i < size; ++i) + { + for (UInt32 value = start / TIME_SLOT_SIZE; value <= (start + durations[i]) / TIME_SLOT_SIZE; ++value) + { + result_values.push_back(value * TIME_SLOT_SIZE); + ++current_offset; + } + + result_offsets[i] = current_offset; + } + } + + static void constant_constant( + UInt32 start, DurationType duration, + Array & result) + { + for (UInt32 value = start / TIME_SLOT_SIZE; value <= (start + duration) / TIME_SLOT_SIZE; ++value) + result.push_back(static_cast(value * TIME_SLOT_SIZE)); + } +}; + + +class FunctionTimeSlots : public IFunction +{ +public: + static constexpr auto name = "timeSlots"; + static FunctionPtr create(const Context &) { return std::make_shared(); } + + String getName() const override + { + return name; + } + + size_t getNumberOfArguments() const override { return 2; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!WhichDataType(arguments[0]).isDateTime()) + throw Exception("Illegal type " + arguments[0]->getName() + " of first argument of function " + getName() + ". Must be DateTime.", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + if (!WhichDataType(arguments[1]).isUInt32()) + throw Exception("Illegal type " + arguments[1]->getName() + " of second argument of function " + getName() + ". Must be UInt32.", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + return std::make_shared(std::make_shared()); + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + auto starts = checkAndGetColumn(block.getByPosition(arguments[0]).column.get()); + auto const_starts = checkAndGetColumnConst(block.getByPosition(arguments[0]).column.get()); + + auto durations = checkAndGetColumn(block.getByPosition(arguments[1]).column.get()); + auto const_durations = checkAndGetColumnConst(block.getByPosition(arguments[1]).column.get()); + + auto res = ColumnArray::create(ColumnUInt32::create()); + ColumnUInt32::Container & res_values = typeid_cast(res->getData()).getData(); + + if (starts && durations) + { + TimeSlotsImpl::vector_vector(starts->getData(), durations->getData(), res_values, res->getOffsets()); + block.getByPosition(result).column = std::move(res); + } + else if (starts && const_durations) + { + TimeSlotsImpl::vector_constant(starts->getData(), const_durations->getValue(), res_values, res->getOffsets()); + block.getByPosition(result).column = std::move(res); + } + else if (const_starts && durations) + { + TimeSlotsImpl::constant_vector(const_starts->getValue(), durations->getData(), res_values, res->getOffsets()); + block.getByPosition(result).column = std::move(res); + } + else if (const_starts && const_durations) + { + Array const_res; + TimeSlotsImpl::constant_constant(const_starts->getValue(), const_durations->getValue(), const_res); + block.getByPosition(result).column = block.getByPosition(result).type->createColumnConst(input_rows_count, const_res); + } + else + throw Exception("Illegal columns " + block.getByPosition(arguments[0]).column->getName() + + ", " + block.getByPosition(arguments[1]).column->getName() + + " of arguments of function " + getName(), + ErrorCodes::ILLEGAL_COLUMN); + } +}; + +void registerFunctionTimeSlots(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} diff --git a/dbms/src/Functions/toDayOfMonth.cpp b/dbms/src/Functions/toDayOfMonth.cpp new file mode 100644 index 00000000000..5bd9cf42ff4 --- /dev/null +++ b/dbms/src/Functions/toDayOfMonth.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToDayOfMonth = FunctionDateOrDateTimeToSomething; + +void registerFunctionToDayOfMonth(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toDayOfWeek.cpp b/dbms/src/Functions/toDayOfWeek.cpp new file mode 100644 index 00000000000..34c0311d695 --- /dev/null +++ b/dbms/src/Functions/toDayOfWeek.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToDayOfWeek = FunctionDateOrDateTimeToSomething; + +void registerFunctionToDayOfWeek(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toDayOfYear.cpp b/dbms/src/Functions/toDayOfYear.cpp new file mode 100644 index 00000000000..d20cd28110f --- /dev/null +++ b/dbms/src/Functions/toDayOfYear.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToDayOfYear = FunctionDateOrDateTimeToSomething; + +void registerFunctionToDayOfYear(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toHour.cpp b/dbms/src/Functions/toHour.cpp new file mode 100644 index 00000000000..c056c95a222 --- /dev/null +++ b/dbms/src/Functions/toHour.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToHour = FunctionDateOrDateTimeToSomething; + +void registerFunctionToHour(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toISOWeek.cpp b/dbms/src/Functions/toISOWeek.cpp new file mode 100644 index 00000000000..451bd7e11eb --- /dev/null +++ b/dbms/src/Functions/toISOWeek.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToISOWeek = FunctionDateOrDateTimeToSomething; + +void registerFunctionToISOWeek(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toISOYear.cpp b/dbms/src/Functions/toISOYear.cpp new file mode 100644 index 00000000000..320994d20bd --- /dev/null +++ b/dbms/src/Functions/toISOYear.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToISOYear = FunctionDateOrDateTimeToSomething; + +void registerFunctionToISOYear(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toMinute.cpp b/dbms/src/Functions/toMinute.cpp new file mode 100644 index 00000000000..ab0c9792913 --- /dev/null +++ b/dbms/src/Functions/toMinute.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToMinute = FunctionDateOrDateTimeToSomething; + +void registerFunctionToMinute(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toMonday.cpp b/dbms/src/Functions/toMonday.cpp new file mode 100644 index 00000000000..579e760721c --- /dev/null +++ b/dbms/src/Functions/toMonday.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToMonday = FunctionDateOrDateTimeToSomething; + +void registerFunctionToMonday(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toMonth.cpp b/dbms/src/Functions/toMonth.cpp new file mode 100644 index 00000000000..d14b6f68cd0 --- /dev/null +++ b/dbms/src/Functions/toMonth.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToMonth = FunctionDateOrDateTimeToSomething; + +void registerFunctionToMonth(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toQuarter.cpp b/dbms/src/Functions/toQuarter.cpp new file mode 100644 index 00000000000..7f1c3d830f2 --- /dev/null +++ b/dbms/src/Functions/toQuarter.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToQuarter = FunctionDateOrDateTimeToSomething; + +void registerFunctionToQuarter(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toRelativeDayNum.cpp b/dbms/src/Functions/toRelativeDayNum.cpp new file mode 100644 index 00000000000..51410b1fbc9 --- /dev/null +++ b/dbms/src/Functions/toRelativeDayNum.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToRelativeDayNum = FunctionDateOrDateTimeToSomething; + +void registerFunctionToRelativeDayNum(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toRelativeHourNum.cpp b/dbms/src/Functions/toRelativeHourNum.cpp new file mode 100644 index 00000000000..f560f965137 --- /dev/null +++ b/dbms/src/Functions/toRelativeHourNum.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToRelativeHourNum = FunctionDateOrDateTimeToSomething; + +void registerFunctionToRelativeHourNum(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toRelativeMinuteNum.cpp b/dbms/src/Functions/toRelativeMinuteNum.cpp new file mode 100644 index 00000000000..8b787e3ca59 --- /dev/null +++ b/dbms/src/Functions/toRelativeMinuteNum.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToRelativeMinuteNum = FunctionDateOrDateTimeToSomething; + +void registerFunctionToRelativeMinuteNum(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toRelativeMonthNum.cpp b/dbms/src/Functions/toRelativeMonthNum.cpp new file mode 100644 index 00000000000..ea0f186642b --- /dev/null +++ b/dbms/src/Functions/toRelativeMonthNum.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToRelativeMonthNum = FunctionDateOrDateTimeToSomething; + +void registerFunctionToRelativeMonthNum(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toRelativeQuarterNum.cpp b/dbms/src/Functions/toRelativeQuarterNum.cpp new file mode 100644 index 00000000000..45b536c8c68 --- /dev/null +++ b/dbms/src/Functions/toRelativeQuarterNum.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToRelativeQuarterNum = FunctionDateOrDateTimeToSomething; + +void registerFunctionToRelativeQuarterNum(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toRelativeSecondNum.cpp b/dbms/src/Functions/toRelativeSecondNum.cpp new file mode 100644 index 00000000000..78aa6254eb0 --- /dev/null +++ b/dbms/src/Functions/toRelativeSecondNum.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToRelativeSecondNum = FunctionDateOrDateTimeToSomething; + +void registerFunctionToRelativeSecondNum(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toRelativeWeekNum.cpp b/dbms/src/Functions/toRelativeWeekNum.cpp new file mode 100644 index 00000000000..441ad50f5c6 --- /dev/null +++ b/dbms/src/Functions/toRelativeWeekNum.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToRelativeWeekNum = FunctionDateOrDateTimeToSomething; + +void registerFunctionToRelativeWeekNum(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toRelativeYearNum.cpp b/dbms/src/Functions/toRelativeYearNum.cpp new file mode 100644 index 00000000000..abe81a7b634 --- /dev/null +++ b/dbms/src/Functions/toRelativeYearNum.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToRelativeYearNum = FunctionDateOrDateTimeToSomething; + +void registerFunctionToRelativeYearNum(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toSecond.cpp b/dbms/src/Functions/toSecond.cpp new file mode 100644 index 00000000000..e0bdcecdefb --- /dev/null +++ b/dbms/src/Functions/toSecond.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToSecond = FunctionDateOrDateTimeToSomething; + +void registerFunctionToSecond(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toStartOfDay.cpp b/dbms/src/Functions/toStartOfDay.cpp new file mode 100644 index 00000000000..873c768de0e --- /dev/null +++ b/dbms/src/Functions/toStartOfDay.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToStartOfDay = FunctionDateOrDateTimeToSomething; + +void registerFunctionToStartOfDay(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toStartOfFifteenMinutes.cpp b/dbms/src/Functions/toStartOfFifteenMinutes.cpp new file mode 100644 index 00000000000..1ad52ad3ca2 --- /dev/null +++ b/dbms/src/Functions/toStartOfFifteenMinutes.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToStartOfFifteenMinutes = FunctionDateOrDateTimeToSomething; + +void registerFunctionToStartOfFifteenMinutes(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toStartOfFiveMinute.cpp b/dbms/src/Functions/toStartOfFiveMinute.cpp new file mode 100644 index 00000000000..758581ea409 --- /dev/null +++ b/dbms/src/Functions/toStartOfFiveMinute.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToStartOfFiveMinute = FunctionDateOrDateTimeToSomething; + +void registerFunctionToStartOfFiveMinute(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toStartOfHour.cpp b/dbms/src/Functions/toStartOfHour.cpp new file mode 100644 index 00000000000..e074ac3cd74 --- /dev/null +++ b/dbms/src/Functions/toStartOfHour.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToStartOfHour = FunctionDateOrDateTimeToSomething; + +void registerFunctionToStartOfHour(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toStartOfISOYear.cpp b/dbms/src/Functions/toStartOfISOYear.cpp new file mode 100644 index 00000000000..3288460e810 --- /dev/null +++ b/dbms/src/Functions/toStartOfISOYear.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToStartOfISOYear = FunctionDateOrDateTimeToSomething; + +void registerFunctionToStartOfISOYear(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toStartOfMinute.cpp b/dbms/src/Functions/toStartOfMinute.cpp new file mode 100644 index 00000000000..60b75371303 --- /dev/null +++ b/dbms/src/Functions/toStartOfMinute.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToStartOfMinute = FunctionDateOrDateTimeToSomething; + +void registerFunctionToStartOfMinute(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toStartOfMonth.cpp b/dbms/src/Functions/toStartOfMonth.cpp new file mode 100644 index 00000000000..1250b2ecf24 --- /dev/null +++ b/dbms/src/Functions/toStartOfMonth.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToStartOfMonth = FunctionDateOrDateTimeToSomething; + +void registerFunctionToStartOfMonth(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toStartOfQuarter.cpp b/dbms/src/Functions/toStartOfQuarter.cpp new file mode 100644 index 00000000000..e18f156d9ac --- /dev/null +++ b/dbms/src/Functions/toStartOfQuarter.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToStartOfQuarter = FunctionDateOrDateTimeToSomething; + +void registerFunctionToStartOfQuarter(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toStartOfYear.cpp b/dbms/src/Functions/toStartOfYear.cpp new file mode 100644 index 00000000000..48d26c705ee --- /dev/null +++ b/dbms/src/Functions/toStartOfYear.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToStartOfYear = FunctionDateOrDateTimeToSomething; + +void registerFunctionToStartOfYear(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toTime.cpp b/dbms/src/Functions/toTime.cpp new file mode 100644 index 00000000000..99cf1f29f02 --- /dev/null +++ b/dbms/src/Functions/toTime.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToTime = FunctionDateOrDateTimeToSomething; + +void registerFunctionToTime(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toTimeZone.cpp b/dbms/src/Functions/toTimeZone.cpp new file mode 100644 index 00000000000..d18b3d88e7b --- /dev/null +++ b/dbms/src/Functions/toTimeZone.cpp @@ -0,0 +1,59 @@ +#include + +#include +#include +#include + +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; + extern const int ILLEGAL_TYPE_OF_ARGUMENT; +} + +/// Just changes time zone information for data type. The calculation is free. +class FunctionToTimeZone : public IFunction +{ +public: + static constexpr auto name = "toTimeZone"; + static FunctionPtr create(const Context &) { return std::make_shared(); } + + String getName() const override + { + return name; + } + + size_t getNumberOfArguments() const override { return 2; } + + DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override + { + if (arguments.size() != 2) + throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + + toString(arguments.size()) + ", should be 2", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + if (!WhichDataType(arguments[0].type).isDateTime()) + throw Exception{"Illegal type " + arguments[0].type->getName() + " of argument of function " + getName() + + ". Should be DateTime", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT}; + + String time_zone_name = extractTimeZoneNameFromFunctionArguments(arguments, 1, 0); + return std::make_shared(time_zone_name); + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override + { + block.getByPosition(result).column = block.getByPosition(arguments[0]).column; + } +}; + +void registerFunctionToTimeZone(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} diff --git a/dbms/src/Functions/toYYYYMM.cpp b/dbms/src/Functions/toYYYYMM.cpp new file mode 100644 index 00000000000..bf02a54f48f --- /dev/null +++ b/dbms/src/Functions/toYYYYMM.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToYYYYMM = FunctionDateOrDateTimeToSomething; + +void registerFunctionToYYYYMM(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toYYYYMMDD.cpp b/dbms/src/Functions/toYYYYMMDD.cpp new file mode 100644 index 00000000000..973bc78c943 --- /dev/null +++ b/dbms/src/Functions/toYYYYMMDD.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToYYYYMMDD = FunctionDateOrDateTimeToSomething; + +void registerFunctionToYYYYMMDD(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toYYYYMMDDhhmmss.cpp b/dbms/src/Functions/toYYYYMMDDhhmmss.cpp new file mode 100644 index 00000000000..d85f0658b67 --- /dev/null +++ b/dbms/src/Functions/toYYYYMMDDhhmmss.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToYYYYMMDDhhmmss = FunctionDateOrDateTimeToSomething; + +void registerFunctionToYYYYMMDDhhmmss(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/toYear.cpp b/dbms/src/Functions/toYear.cpp new file mode 100644 index 00000000000..d0771a90dd5 --- /dev/null +++ b/dbms/src/Functions/toYear.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +using FunctionToYear = FunctionDateOrDateTimeToSomething; + +void registerFunctionToYear(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + + diff --git a/dbms/src/Functions/today.cpp b/dbms/src/Functions/today.cpp new file mode 100644 index 00000000000..7a72f4d547f --- /dev/null +++ b/dbms/src/Functions/today.cpp @@ -0,0 +1,45 @@ +#include + +#include + +#include +#include + + +namespace DB +{ + +class FunctionToday : public IFunction +{ +public: + static constexpr auto name = "today"; + static FunctionPtr create(const Context &) { return std::make_shared(); } + + String getName() const override + { + return name; + } + + size_t getNumberOfArguments() const override { return 0; } + + DataTypePtr getReturnTypeImpl(const DataTypes & /*arguments*/) const override + { + return std::make_shared(); + } + + bool isDeterministic() const override { return false; } + + void executeImpl(Block & block, const ColumnNumbers &, size_t result, size_t input_rows_count) override + { + block.getByPosition(result).column = DataTypeDate().createColumnConst( + input_rows_count, + UInt64(DateLUT::instance().toDayNum(time(nullptr)))); + } +}; + +void registerFunctionToday(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} diff --git a/dbms/src/Functions/yesterday.cpp b/dbms/src/Functions/yesterday.cpp new file mode 100644 index 00000000000..506f84c863c --- /dev/null +++ b/dbms/src/Functions/yesterday.cpp @@ -0,0 +1,45 @@ +#include + +#include + +#include +#include + + +namespace DB +{ + +class FunctionYesterday : public IFunction +{ +public: + static constexpr auto name = "yesterday"; + static FunctionPtr create(const Context &) { return std::make_shared(); } + + String getName() const override + { + return name; + } + + size_t getNumberOfArguments() const override { return 0; } + + DataTypePtr getReturnTypeImpl(const DataTypes & /*arguments*/) const override + { + return std::make_shared(); + } + + bool isDeterministic() const override { return false; } + + void executeImpl(Block & block, const ColumnNumbers &, size_t result, size_t input_rows_count) override + { + block.getByPosition(result).column = DataTypeDate().createColumnConst( + input_rows_count, + UInt64(DateLUT::instance().toDayNum(time(nullptr)) - 1)); + } +}; + +void registerFunctionYesterday(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} From 0e7caa05fa0f40d58bb36190888be064f3c82f5b Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 26 Sep 2018 03:32:38 +0300 Subject: [PATCH 6/6] Every function in its own file, part 5 [#CLICKHOUSE-2] --- dbms/src/Functions/FunctionsDateTime.cpp | 123 - dbms/src/Functions/FunctionsDateTime.h | 2166 ----------------- .../Functions/registerFunctionsDateTime.cpp | 123 + 3 files changed, 123 insertions(+), 2289 deletions(-) delete mode 100644 dbms/src/Functions/FunctionsDateTime.cpp delete mode 100644 dbms/src/Functions/FunctionsDateTime.h create mode 100644 dbms/src/Functions/registerFunctionsDateTime.cpp diff --git a/dbms/src/Functions/FunctionsDateTime.cpp b/dbms/src/Functions/FunctionsDateTime.cpp deleted file mode 100644 index f89d82b241e..00000000000 --- a/dbms/src/Functions/FunctionsDateTime.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include - -namespace DB -{ - - -static std::string extractTimeZoneNameFromColumn(const IColumn & column) -{ - const ColumnConst * time_zone_column = checkAndGetColumnConst(&column); - - if (!time_zone_column) - throw Exception("Illegal column " + column.getName() - + " of time zone argument of function, must be constant string", - ErrorCodes::ILLEGAL_COLUMN); - - return time_zone_column->getValue(); -} - - -std::string extractTimeZoneNameFromFunctionArguments(const ColumnsWithTypeAndName & arguments, size_t time_zone_arg_num, size_t datetime_arg_num) -{ - /// Explicit time zone may be passed in last argument. - if (arguments.size() == time_zone_arg_num + 1 && arguments[time_zone_arg_num].column) - { - return extractTimeZoneNameFromColumn(*arguments[time_zone_arg_num].column); - } - else - { - if (!arguments.size()) - return {}; - - /// If time zone is attached to an argument of type DateTime. - if (const DataTypeDateTime * type = checkAndGetDataType(arguments[datetime_arg_num].type.get())) - return type->getTimeZone().getTimeZone(); - - return {}; - } -} - -const DateLUTImpl & extractTimeZoneFromFunctionArguments(Block & block, const ColumnNumbers & arguments, size_t time_zone_arg_num, size_t datetime_arg_num) -{ - if (arguments.size() == time_zone_arg_num + 1) - return DateLUT::instance(extractTimeZoneNameFromColumn(*block.getByPosition(arguments[time_zone_arg_num]).column)); - else - { - if (!arguments.size()) - return DateLUT::instance(); - - /// If time zone is attached to an argument of type DateTime. - if (const DataTypeDateTime * type = checkAndGetDataType(block.getByPosition(arguments[datetime_arg_num]).type.get())) - return type->getTimeZone(); - - return DateLUT::instance(); - } -} - -void registerFunctionsDateTime(FunctionFactory & factory) -{ - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(FunctionFactory::CaseInsensitive); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - factory.registerFunction(); - - factory.registerFunction(FunctionFactory::CaseInsensitive); - - factory.registerFunction(); - - - factory.registerFunction(); -} - -} diff --git a/dbms/src/Functions/FunctionsDateTime.h b/dbms/src/Functions/FunctionsDateTime.h deleted file mode 100644 index ffeaa237388..00000000000 --- a/dbms/src/Functions/FunctionsDateTime.h +++ /dev/null @@ -1,2166 +0,0 @@ -#pragma once - -#include -#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 BAD_ARGUMENTS; - extern const int ILLEGAL_COLUMN; - extern const int NOT_IMPLEMENTED; -} - -/** Functions for working with date and time. - * - * toYear, toMonth, toDayOfMonth, toDayOfWeek, toHour, toMinute, toSecond, - * toMonday, toStartOfMonth, toStartOfYear, toStartOfMinute, toStartOfFiveMinute, toStartOfFifteenMinutes - * toStartOfHour, toTime, - * now, today, yesterday - * TODO: makeDate, makeDateTime - * - * (toDate - located in FunctionConversion.h file) - * - * Return types: - * toYear -> UInt16 - * toMonth, toDayOfMonth, toDayOfWeek, toHour, toMinute, toSecond -> UInt8 - * toMonday, toStartOfMonth, toStartOfYear -> Date - * toStartOfMinute, toStartOfHour, toTime, now -> DateTime - * - * And also: - * - * timeSlot(EventTime) - * - rounds the time to half an hour. - * - * timeSlots(StartTime, Duration) - * - for the time interval beginning at `StartTime` and continuing `Duration` seconds, - * returns an array of time points, consisting of rounding down to half an hour of points from this interval. - * For example, timeSlots(toDateTime('2012-01-01 12:20:00'), 600) = [toDateTime('2012-01-01 12:00:00'), toDateTime('2012-01-01 12:30:00')]. - * This is necessary to search for hits that are part of the corresponding visit. - */ - - -/// Determine working timezone either from optional argument with time zone name or from time zone in DateTime type of argument. -std::string extractTimeZoneNameFromFunctionArguments(const ColumnsWithTypeAndName & arguments, size_t time_zone_arg_num, size_t datetime_arg_num); -const DateLUTImpl & extractTimeZoneFromFunctionArguments(Block & block, const ColumnNumbers & arguments, size_t time_zone_arg_num, size_t datetime_arg_num); - - - -#define TIME_SLOT_SIZE 1800 - -/** Transformations. - * Represents two functions - from datetime (UInt32) and from date (UInt16). - * - * Also, the "factor transformation" F is defined for the T transformation. - * This is a transformation of F such that its value identifies the region of monotonicity for T - * (for a fixed value of F, the transformation T is monotonic). - * - * Or, figuratively, if T is similar to taking the remainder of division, then F is similar to division. - * - * Example: for transformation T "get the day number in the month" (2015-02-03 -> 3), - * factor-transformation F is "round to the nearest month" (2015-02-03 -> 2015-02-01). - */ - -/// This factor transformation will say that the function is monotone everywhere. -struct ZeroTransform -{ - static inline UInt16 execute(UInt32, const DateLUTImpl &) { return 0; } - static inline UInt16 execute(UInt16, const DateLUTImpl &) { return 0; } -}; - -struct ToDateImpl -{ - static constexpr auto name = "toDate"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return UInt16(time_zone.toDayNum(t)); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl &) - { - return d; - } - - using FactorTransform = ZeroTransform; -}; - -struct ToStartOfDayImpl -{ - static constexpr auto name = "toStartOfDay"; - - static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toDate(t); - } - static inline UInt32 execute(UInt16, const DateLUTImpl &) - { - throw Exception("Illegal type Date of argument for function toStartOfDay", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToMondayImpl -{ - static constexpr auto name = "toMonday"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toFirstDayNumOfWeek(time_zone.toDayNum(t)); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toFirstDayNumOfWeek(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToStartOfMonthImpl -{ - static constexpr auto name = "toStartOfMonth"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toFirstDayNumOfMonth(time_zone.toDayNum(t)); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toFirstDayNumOfMonth(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToStartOfQuarterImpl -{ - static constexpr auto name = "toStartOfQuarter"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toFirstDayNumOfQuarter(time_zone.toDayNum(t)); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toFirstDayNumOfQuarter(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToStartOfYearImpl -{ - static constexpr auto name = "toStartOfYear"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toFirstDayNumOfYear(time_zone.toDayNum(t)); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toFirstDayNumOfYear(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - - -struct ToTimeImpl -{ - static constexpr auto name = "toTime"; - - /// When transforming to time, the date will be equated to 1970-01-02. - static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toTime(t) + 86400; - } - - static inline UInt32 execute(UInt16, const DateLUTImpl &) - { - throw Exception("Illegal type Date of argument for function toTime", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } - - using FactorTransform = ToDateImpl; -}; - -struct ToStartOfMinuteImpl -{ - static constexpr auto name = "toStartOfMinute"; - - static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toStartOfMinute(t); - } - static inline UInt32 execute(UInt16, const DateLUTImpl &) - { - throw Exception("Illegal type Date of argument for function toStartOfMinute", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToStartOfFiveMinuteImpl -{ - static constexpr auto name = "toStartOfFiveMinute"; - - static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toStartOfFiveMinute(t); - } - static inline UInt32 execute(UInt16, const DateLUTImpl &) - { - throw Exception("Illegal type Date of argument for function toStartOfFiveMinute", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToStartOfFifteenMinutesImpl -{ - static constexpr auto name = "toStartOfFifteenMinutes"; - - static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toStartOfFifteenMinutes(t); - } - static inline UInt32 execute(UInt16, const DateLUTImpl &) - { - throw Exception("Illegal type Date of argument for function toStartOfFifteenMinutes", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToStartOfHourImpl -{ - static constexpr auto name = "toStartOfHour"; - - static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toStartOfHour(t); - } - - static inline UInt32 execute(UInt16, const DateLUTImpl &) - { - throw Exception("Illegal type Date of argument for function toStartOfHour", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToYearImpl -{ - static constexpr auto name = "toYear"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toYear(t); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toYear(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToQuarterImpl -{ - static constexpr auto name = "toQuarter"; - - static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toQuarter(t); - } - static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toQuarter(DayNum(d)); - } - - using FactorTransform = ToStartOfYearImpl; -}; - -struct ToMonthImpl -{ - static constexpr auto name = "toMonth"; - - static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toMonth(t); - } - static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toMonth(DayNum(d)); - } - - using FactorTransform = ToStartOfYearImpl; -}; - -struct ToDayOfMonthImpl -{ - static constexpr auto name = "toDayOfMonth"; - - static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toDayOfMonth(t); - } - static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toDayOfMonth(DayNum(d)); - } - - using FactorTransform = ToStartOfMonthImpl; -}; - -struct ToDayOfWeekImpl -{ - static constexpr auto name = "toDayOfWeek"; - - static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toDayOfWeek(t); - } - static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toDayOfWeek(DayNum(d)); - } - - using FactorTransform = ToMondayImpl; -}; - -struct ToDayOfYearImpl -{ - static constexpr auto name = "toDayOfYear"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toDayOfYear(t); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toDayOfYear(DayNum(d)); - } - - using FactorTransform = ToStartOfYearImpl; -}; - -struct ToHourImpl -{ - static constexpr auto name = "toHour"; - - static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toHour(t); - } - - static inline UInt8 execute(UInt16, const DateLUTImpl &) - { - throw Exception("Illegal type Date of argument for function toHour", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } - - using FactorTransform = ToDateImpl; -}; - -struct ToMinuteImpl -{ - static constexpr auto name = "toMinute"; - - static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toMinute(t); - } - static inline UInt8 execute(UInt16, const DateLUTImpl &) - { - throw Exception("Illegal type Date of argument for function toMinute", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } - - using FactorTransform = ToStartOfHourImpl; -}; - -struct ToSecondImpl -{ - static constexpr auto name = "toSecond"; - - static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toSecond(t); - } - static inline UInt8 execute(UInt16, const DateLUTImpl &) - { - throw Exception("Illegal type Date of argument for function toSecond", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } - - using FactorTransform = ToStartOfMinuteImpl; -}; - -struct ToISOYearImpl -{ - static constexpr auto name = "toISOYear"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toISOYear(time_zone.toDayNum(t)); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toISOYear(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToStartOfISOYearImpl -{ - static constexpr auto name = "toStartOfISOYear"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toFirstDayNumOfISOYear(time_zone.toDayNum(t)); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toFirstDayNumOfISOYear(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToISOWeekImpl -{ - static constexpr auto name = "toISOWeek"; - - static inline UInt8 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toISOWeek(time_zone.toDayNum(t)); - } - static inline UInt8 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toISOWeek(DayNum(d)); - } - - using FactorTransform = ToISOYearImpl; -}; - -struct ToRelativeYearNumImpl -{ - static constexpr auto name = "toRelativeYearNum"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toYear(t); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toYear(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToRelativeQuarterNumImpl -{ - static constexpr auto name = "toRelativeQuarterNum"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toRelativeQuarterNum(t); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toRelativeQuarterNum(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToRelativeMonthNumImpl -{ - static constexpr auto name = "toRelativeMonthNum"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toRelativeMonthNum(t); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toRelativeMonthNum(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToRelativeWeekNumImpl -{ - static constexpr auto name = "toRelativeWeekNum"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toRelativeWeekNum(t); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toRelativeWeekNum(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToRelativeDayNumImpl -{ - static constexpr auto name = "toRelativeDayNum"; - - static inline UInt16 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toDayNum(t); - } - static inline UInt16 execute(UInt16 d, const DateLUTImpl &) - { - return static_cast(d); - } - - using FactorTransform = ZeroTransform; -}; - - -struct ToRelativeHourNumImpl -{ - static constexpr auto name = "toRelativeHourNum"; - - static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toRelativeHourNum(t); - } - static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toRelativeHourNum(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToRelativeMinuteNumImpl -{ - static constexpr auto name = "toRelativeMinuteNum"; - - static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toRelativeMinuteNum(t); - } - static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toRelativeMinuteNum(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToRelativeSecondNumImpl -{ - static constexpr auto name = "toRelativeSecondNum"; - - static inline UInt32 execute(UInt32 t, const DateLUTImpl &) - { - return t; - } - static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.fromDayNum(DayNum(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToYYYYMMImpl -{ - static constexpr auto name = "toYYYYMM"; - - static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toNumYYYYMM(t); - } - static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toNumYYYYMM(static_cast(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToYYYYMMDDImpl -{ - static constexpr auto name = "toYYYYMMDD"; - - static inline UInt32 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toNumYYYYMMDD(t); - } - static inline UInt32 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toNumYYYYMMDD(static_cast(d)); - } - - using FactorTransform = ZeroTransform; -}; - -struct ToYYYYMMDDhhmmssImpl -{ - static constexpr auto name = "toYYYYMMDDhhmmss"; - - static inline UInt64 execute(UInt32 t, const DateLUTImpl & time_zone) - { - return time_zone.toNumYYYYMMDDhhmmss(t); - } - static inline UInt64 execute(UInt16 d, const DateLUTImpl & time_zone) - { - return time_zone.toNumYYYYMMDDhhmmss(time_zone.toDate(static_cast(d))); - } - - using FactorTransform = ZeroTransform; -}; - - -template -struct Transformer -{ - static void vector(const PaddedPODArray & vec_from, PaddedPODArray & vec_to, const DateLUTImpl & time_zone) - { - size_t size = vec_from.size(); - vec_to.resize(size); - - for (size_t i = 0; i < size; ++i) - vec_to[i] = Transform::execute(vec_from[i], time_zone); - } -}; - - -template -struct DateTimeTransformImpl -{ - static void execute(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) - { - using Op = Transformer; - - const DateLUTImpl & time_zone = extractTimeZoneFromFunctionArguments(block, arguments, 1, 0); - - const ColumnPtr source_col = block.getByPosition(arguments[0]).column; - if (const auto * sources = checkAndGetColumn>(source_col.get())) - { - auto col_to = ColumnVector::create(); - Op::vector(sources->getData(), col_to->getData(), time_zone); - block.getByPosition(result).column = std::move(col_to); - } - else - { - throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() - + " of first argument of function " + Transform::name, - ErrorCodes::ILLEGAL_COLUMN); - } - } -}; - - -template -class FunctionDateOrDateTimeToSomething : public IFunction -{ -public: - static constexpr auto name = Transform::name; - static FunctionPtr create(const Context &) { return std::make_shared(); } - - String getName() const override - { - return name; - } - - bool isVariadic() const override { return true; } - size_t getNumberOfArguments() const override { return 0; } - - DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override - { - if (arguments.size() == 1) - { - if (!isDateOrDateTime(arguments[0].type)) - throw Exception("Illegal type " + arguments[0].type->getName() + " of argument of function " + getName() + - ". Should be a date or a date with time", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } - else if (arguments.size() == 2) - { - if (!WhichDataType(arguments[0].type).isDateTime() - || !WhichDataType(arguments[1].type).isString()) - throw Exception( - "Function " + getName() + " supports 1 or 2 arguments. The 1st argument " - "must be of type Date or DateTime. The 2nd argument (optional) must be " - "a constant string with timezone name. The timezone argument is allowed " - "only when the 1st argument has the type DateTime", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } - else - throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " - + toString(arguments.size()) + ", should be 1 or 2", - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - - /// For DateTime, if time zone is specified, attach it to type. - if (std::is_same_v) - return std::make_shared(extractTimeZoneNameFromFunctionArguments(arguments, 1, 0)); - else - return std::make_shared(); - } - - bool useDefaultImplementationForConstants() const override { return true; } - ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1}; } - - void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override - { - const IDataType * from_type = block.getByPosition(arguments[0]).type.get(); - WhichDataType which(from_type); - - if (which.isDate()) - DateTimeTransformImpl::execute(block, arguments, result, input_rows_count); - else if (which.isDateTime()) - DateTimeTransformImpl::execute(block, arguments, result, input_rows_count); - else - throw Exception("Illegal type " + block.getByPosition(arguments[0]).type->getName() + " of argument of function " + getName(), - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } - - - bool hasInformationAboutMonotonicity() const override - { - return true; - } - - Monotonicity getMonotonicityForRange(const IDataType & type, const Field & left, const Field & right) const override - { - IFunction::Monotonicity is_monotonic { true }; - IFunction::Monotonicity is_not_monotonic; - - if (std::is_same_v) - { - is_monotonic.is_always_monotonic = true; - return is_monotonic; - } - - /// This method is called only if the function has one argument. Therefore, we do not care about the non-local time zone. - const DateLUTImpl & date_lut = DateLUT::instance(); - - if (left.isNull() || right.isNull()) - return is_not_monotonic; - - /// The function is monotonous on the [left, right] segment, if the factor transformation returns the same values for them. - - if (checkAndGetDataType(&type)) - { - return Transform::FactorTransform::execute(UInt16(left.get()), date_lut) - == Transform::FactorTransform::execute(UInt16(right.get()), date_lut) - ? is_monotonic : is_not_monotonic; - } - else - { - return Transform::FactorTransform::execute(UInt32(left.get()), date_lut) - == Transform::FactorTransform::execute(UInt32(right.get()), date_lut) - ? is_monotonic : is_not_monotonic; - } - } -}; - - -struct AddSecondsImpl -{ - static constexpr auto name = "addSeconds"; - - static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) - { - return t + delta; - } - - static inline UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) - { - return time_zone.fromDayNum(DayNum(d)) + delta; - } -}; - -struct AddMinutesImpl -{ - static constexpr auto name = "addMinutes"; - - static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) - { - return t + delta * 60; - } - - static inline UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) - { - return time_zone.fromDayNum(DayNum(d)) + delta * 60; - } -}; - -struct AddHoursImpl -{ - static constexpr auto name = "addHours"; - - static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) - { - return t + delta * 3600; - } - - static inline UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) - { - return time_zone.fromDayNum(DayNum(d)) + delta * 3600; - } -}; - -struct AddDaysImpl -{ - static constexpr auto name = "addDays"; - - static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) - { - return time_zone.addDays(t, delta); - } - - static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl &) - { - return d + delta; - } -}; - -struct AddWeeksImpl -{ - static constexpr auto name = "addWeeks"; - - static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) - { - return time_zone.addWeeks(t, delta); - } - - static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl &) - { - return d + delta * 7; - } -}; - -struct AddMonthsImpl -{ - static constexpr auto name = "addMonths"; - - static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) - { - return time_zone.addMonths(t, delta); - } - - static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) - { - return time_zone.addMonths(DayNum(d), delta); - } -}; - -struct AddYearsImpl -{ - static constexpr auto name = "addYears"; - - static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) - { - return time_zone.addYears(t, delta); - } - - static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) - { - return time_zone.addYears(DayNum(d), delta); - } -}; - - -template -struct SubtractIntervalImpl -{ - static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) - { - return Transform::execute(t, -delta, time_zone); - } - - static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) - { - return Transform::execute(d, -delta, time_zone); - } -}; - -struct SubtractSecondsImpl : SubtractIntervalImpl { static constexpr auto name = "subtractSeconds"; }; -struct SubtractMinutesImpl : SubtractIntervalImpl { static constexpr auto name = "subtractMinutes"; }; -struct SubtractHoursImpl : SubtractIntervalImpl { static constexpr auto name = "subtractHours"; }; -struct SubtractDaysImpl : SubtractIntervalImpl { static constexpr auto name = "subtractDays"; }; -struct SubtractWeeksImpl : SubtractIntervalImpl { static constexpr auto name = "subtractWeeks"; }; -struct SubtractMonthsImpl : SubtractIntervalImpl { static constexpr auto name = "subtractMonths"; }; -struct SubtractYearsImpl : SubtractIntervalImpl { static constexpr auto name = "subtractYears"; }; - - -template -struct Adder -{ - static void vector_vector(const PaddedPODArray & vec_from, PaddedPODArray & vec_to, const IColumn & delta, const DateLUTImpl & time_zone) - { - size_t size = vec_from.size(); - vec_to.resize(size); - - for (size_t i = 0; i < size; ++i) - vec_to[i] = Transform::execute(vec_from[i], delta.getInt(i), time_zone); - } - - static void vector_constant(const PaddedPODArray & vec_from, PaddedPODArray & vec_to, Int64 delta, const DateLUTImpl & time_zone) - { - size_t size = vec_from.size(); - vec_to.resize(size); - - for (size_t i = 0; i < size; ++i) - vec_to[i] = Transform::execute(vec_from[i], delta, time_zone); - } - - static void constant_vector(const FromType & from, PaddedPODArray & vec_to, const IColumn & delta, const DateLUTImpl & time_zone) - { - size_t size = delta.size(); - vec_to.resize(size); - - for (size_t i = 0; i < size; ++i) - vec_to[i] = Transform::execute(from, delta.getInt(i), time_zone); - } -}; - - -template -struct DateTimeAddIntervalImpl -{ - static void execute(Block & block, const ColumnNumbers & arguments, size_t result) - { - using ToType = decltype(Transform::execute(FromType(), 0, std::declval())); - using Op = Adder; - - const DateLUTImpl & time_zone = extractTimeZoneFromFunctionArguments(block, arguments, 2, 0); - - const ColumnPtr source_col = block.getByPosition(arguments[0]).column; - - if (const auto * sources = checkAndGetColumn>(source_col.get())) - { - auto col_to = ColumnVector::create(); - - const IColumn & delta_column = *block.getByPosition(arguments[1]).column; - - if (const auto * delta_const_column = typeid_cast(&delta_column)) - Op::vector_constant(sources->getData(), col_to->getData(), delta_const_column->getField().get(), time_zone); - else - Op::vector_vector(sources->getData(), col_to->getData(), delta_column, time_zone); - - block.getByPosition(result).column = std::move(col_to); - } - else if (const auto * sources = checkAndGetColumnConst>(source_col.get())) - { - auto col_to = ColumnVector::create(); - Op::constant_vector(sources->template getValue(), col_to->getData(), *block.getByPosition(arguments[1]).column, time_zone); - block.getByPosition(result).column = std::move(col_to); - } - else - { - throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() - + " of first argument of function " + Transform::name, - ErrorCodes::ILLEGAL_COLUMN); - } - } -}; - - -template -class FunctionDateOrDateTimeAddInterval : public IFunction -{ -public: - static constexpr auto name = Transform::name; - static FunctionPtr create(const Context &) { return std::make_shared(); } - - String getName() const override - { - return name; - } - - 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 (!isNumber(arguments[1].type)) - throw Exception("Second argument for function " + getName() + " (delta) must be number", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - if (arguments.size() == 2) - { - if (!isDateOrDateTime(arguments[0].type)) - throw Exception{"Illegal type " + arguments[0].type->getName() + " of argument of function " + getName() + - ". Should be a date or a date with time", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT}; - } - else - { - if (!WhichDataType(arguments[0].type).isDateTime() - || !WhichDataType(arguments[2].type).isString()) - throw Exception( - "Function " + getName() + " supports 2 or 3 arguments. The 1st argument " - "must be of type Date or DateTime. The 2nd argument must be number. " - "The 3rd argument (optional) must be " - "a constant string with timezone name. The timezone argument is allowed " - "only when the 1st argument has the type DateTime", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } - - if (WhichDataType(arguments[0].type).isDate()) - { - if (std::is_same_v())), UInt16>) - return std::make_shared(); - else - return std::make_shared(extractTimeZoneNameFromFunctionArguments(arguments, 2, 0)); - } - else - { - if (std::is_same_v())), UInt16>) - return std::make_shared(); - else - return std::make_shared(extractTimeZoneNameFromFunctionArguments(arguments, 2, 0)); - } - } - - bool useDefaultImplementationForConstants() const override { return true; } - ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {2}; } - - void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override - { - const IDataType * from_type = block.getByPosition(arguments[0]).type.get(); - WhichDataType which(from_type); - - if (which.isDate()) - DateTimeAddIntervalImpl::execute(block, arguments, result); - else if (which.isDateTime()) - DateTimeAddIntervalImpl::execute(block, arguments, result); - else - throw Exception("Illegal type " + block.getByPosition(arguments[0]).type->getName() + " of argument of function " + getName(), - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - } -}; - - -/** dateDiff('unit', t1, t2, [timezone]) - * t1 and t2 can be Date or DateTime - * - * If timezone is specified, it applied to both arguments. - * If not, timezones from datatypes t1 and t2 are used. - * If that timezones are not the same, the result is unspecified. - * - * Timezone matters because days can have different length. - */ -class FunctionDateDiff : public IFunction -{ -public: - static constexpr auto name = "dateDiff"; - static FunctionPtr create(const Context &) { return std::make_shared(); } - - String getName() const override - { - return name; - } - - bool isVariadic() const override { return true; } - size_t getNumberOfArguments() const override { return 0; } - - DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override - { - if (arguments.size() != 3 && arguments.size() != 4) - throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " - + toString(arguments.size()) + ", should be 3 or 4", - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - - if (!isString(arguments[0])) - throw Exception("First argument for function " + getName() + " (unit) must be String", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - if (!isDateOrDateTime(arguments[1])) - throw Exception("Second argument for function " + getName() + " must be Date or DateTime", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - if (!isDateOrDateTime(arguments[2])) - throw Exception("Third argument for function " + getName() + " must be Date or DateTime", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - if (arguments.size() == 4 && !isString(arguments[3])) - throw Exception("Fourth argument for function " + getName() + " (timezone) must be String", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - return std::make_shared(); - } - - bool useDefaultImplementationForConstants() const override { return true; } - ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {0, 3}; } - - void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override - { - auto * unit_column = checkAndGetColumnConst(block.getByPosition(arguments[0]).column.get()); - if (!unit_column) - throw Exception("First argument for function " + getName() + " must be constant String", ErrorCodes::ILLEGAL_COLUMN); - - String unit = Poco::toLower(unit_column->getValue()); - - const IColumn & x = *block.getByPosition(arguments[1]).column; - const IColumn & y = *block.getByPosition(arguments[2]).column; - - size_t rows = input_rows_count; - auto res = ColumnInt64::create(rows); - - const DateLUTImpl & timezone_x = extractTimeZoneFromFunctionArguments(block, arguments, 3, 1); - const DateLUTImpl & timezone_y = extractTimeZoneFromFunctionArguments(block, arguments, 3, 2); - - if (unit == "year" || unit == "yy" || unit == "yyyy") - dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); - else if (unit == "quarter" || unit == "qq" || unit == "q") - dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); - else if (unit == "month" || unit == "mm" || unit == "m") - dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); - else if (unit == "week" || unit == "wk" || unit == "ww") - dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); - else if (unit == "day" || unit == "dd" || unit == "d") - dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); - else if (unit == "hour" || unit == "hh") - dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); - else if (unit == "minute" || unit == "mi" || unit == "n") - dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); - else if (unit == "second" || unit == "ss" || unit == "s") - dispatchForColumns(x, y, timezone_x, timezone_y, res->getData()); - else - throw Exception("Function " + getName() + " does not support '" + unit + "' unit", ErrorCodes::BAD_ARGUMENTS); - - block.getByPosition(result).column = std::move(res); - } - -private: - template - void dispatchForColumns( - const IColumn & x, const IColumn & y, - const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, - ColumnInt64::Container & result) - { - if (auto * x_vec = checkAndGetColumn(&x)) - dispatchForSecondColumn(*x_vec, y, timezone_x, timezone_y, result); - else if (auto * x_vec = checkAndGetColumn(&x)) - dispatchForSecondColumn(*x_vec, y, timezone_x, timezone_y, result); - else if (auto * x_const = checkAndGetColumnConst(&x)) - dispatchConstForSecondColumn(x_const->getValue(), y, timezone_x, timezone_y, result); - else if (auto * x_const = checkAndGetColumnConst(&x)) - dispatchConstForSecondColumn(x_const->getValue(), y, timezone_x, timezone_y, result); - else - throw Exception("Illegal column for first argument of function " + getName() + ", must be Date or DateTime", ErrorCodes::ILLEGAL_COLUMN); - } - - template - void dispatchForSecondColumn( - const ColumnVector & x, const IColumn & y, - const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, - ColumnInt64::Container & result) - { - if (auto * y_vec = checkAndGetColumn(&y)) - vector_vector(x, *y_vec, timezone_x, timezone_y, result); - else if (auto * y_vec = checkAndGetColumn(&y)) - vector_vector(x, *y_vec, timezone_x, timezone_y, result); - else if (auto * y_const = checkAndGetColumnConst(&y)) - vector_constant(x, y_const->getValue(), timezone_x, timezone_y, result); - else if (auto * y_const = checkAndGetColumnConst(&y)) - vector_constant(x, y_const->getValue(), timezone_x, timezone_y, result); - else - throw Exception("Illegal column for second argument of function " + getName() + ", must be Date or DateTime", ErrorCodes::ILLEGAL_COLUMN); - } - - template - void dispatchConstForSecondColumn( - T1 x, const IColumn & y, - const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, - ColumnInt64::Container & result) - { - if (auto * y_vec = checkAndGetColumn(&y)) - constant_vector(x, *y_vec, timezone_x, timezone_y, result); - else if (auto * y_vec = checkAndGetColumn(&y)) - constant_vector(x, *y_vec, timezone_x, timezone_y, result); - else - throw Exception("Illegal column for second argument of function " + getName() + ", must be Date or DateTime", ErrorCodes::ILLEGAL_COLUMN); - } - - template - void vector_vector( - const ColumnVector & x, const ColumnVector & y, - const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, - ColumnInt64::Container & result) - { - const auto & x_data = x.getData(); - const auto & y_data = y.getData(); - for (size_t i = 0, size = x.size(); i < size; ++i) - result[i] = calculate(x_data[i], y_data[i], timezone_x, timezone_y); - } - - template - void vector_constant( - const ColumnVector & x, T2 y, - const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, - ColumnInt64::Container & result) - { - const auto & x_data = x.getData(); - for (size_t i = 0, size = x.size(); i < size; ++i) - result[i] = calculate(x_data[i], y, timezone_x, timezone_y); - } - - template - void constant_vector( - T1 x, const ColumnVector & y, - const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, - ColumnInt64::Container & result) - { - const auto & y_data = y.getData(); - for (size_t i = 0, size = y.size(); i < size; ++i) - result[i] = calculate(x, y_data[i], timezone_x, timezone_y); - } - - template - Int64 calculate(T1 x, T2 y, const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y) - { - return Int64(Transform::execute(y, timezone_y)) - - Int64(Transform::execute(x, timezone_x)); - } -}; - - -/// Get the current time. (It is a constant, it is evaluated once for the entire query.) -class FunctionNow : public IFunction -{ -public: - static constexpr auto name = "now"; - static FunctionPtr create(const Context &) { return std::make_shared(); } - - String getName() const override - { - return name; - } - - size_t getNumberOfArguments() const override { return 0; } - - DataTypePtr getReturnTypeImpl(const DataTypes & /*arguments*/) const override - { - return std::make_shared(); - } - - bool isDeterministic() const override { return false; } - - void executeImpl(Block & block, const ColumnNumbers &, size_t result, size_t input_rows_count) override - { - block.getByPosition(result).column = DataTypeUInt32().createColumnConst( - input_rows_count, - static_cast(time(nullptr))); - } -}; - - -class FunctionToday : public IFunction -{ -public: - static constexpr auto name = "today"; - static FunctionPtr create(const Context &) { return std::make_shared(); } - - String getName() const override - { - return name; - } - - size_t getNumberOfArguments() const override { return 0; } - - DataTypePtr getReturnTypeImpl(const DataTypes & /*arguments*/) const override - { - return std::make_shared(); - } - - bool isDeterministic() const override { return false; } - - void executeImpl(Block & block, const ColumnNumbers &, size_t result, size_t input_rows_count) override - { - block.getByPosition(result).column = DataTypeUInt16().createColumnConst( - input_rows_count, - UInt64(DateLUT::instance().toDayNum(time(nullptr)))); - } -}; - - -class FunctionYesterday : public IFunction -{ -public: - static constexpr auto name = "yesterday"; - static FunctionPtr create(const Context &) { return std::make_shared(); } - - String getName() const override - { - return name; - } - - size_t getNumberOfArguments() const override { return 0; } - - DataTypePtr getReturnTypeImpl(const DataTypes & /*arguments*/) const override - { - return std::make_shared(); - } - - bool isDeterministic() const override { return false; } - - void executeImpl(Block & block, const ColumnNumbers &, size_t result, size_t input_rows_count) override - { - block.getByPosition(result).column = DataTypeUInt16().createColumnConst( - input_rows_count, - UInt64(DateLUT::instance().toDayNum(time(nullptr)) - 1)); - } -}; - - -/// Just changes time zone information for data type. The calculation is free. -class FunctionToTimeZone : public IFunction -{ -public: - static constexpr auto name = "toTimeZone"; - static FunctionPtr create(const Context &) { return std::make_shared(); } - - String getName() const override - { - return name; - } - - size_t getNumberOfArguments() const override { return 2; } - - DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override - { - if (arguments.size() != 2) - throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " - + toString(arguments.size()) + ", should be 2", - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - - if (!WhichDataType(arguments[0].type).isDateTime()) - throw Exception{"Illegal type " + arguments[0].type->getName() + " of argument of function " + getName() + - ". Should be DateTime", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT}; - - String time_zone_name = extractTimeZoneNameFromFunctionArguments(arguments, 1, 0); - return std::make_shared(time_zone_name); - } - - void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override - { - block.getByPosition(result).column = block.getByPosition(arguments[0]).column; - } -}; - - -class FunctionTimeSlot : public IFunction -{ -public: - static constexpr auto name = "timeSlot"; - static FunctionPtr create(const Context &) { return std::make_shared(); } - - String getName() const override - { - return name; - } - - size_t getNumberOfArguments() const override { return 1; } - - DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override - { - if (!WhichDataType(arguments[0]).isDateTime()) - throw Exception("Illegal type " + arguments[0]->getName() + " of first argument of function " + getName() + ". Must be DateTime.", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - return std::make_shared(); - } - - bool useDefaultImplementationForConstants() const override { return true; } - - void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override - { - if (const ColumnUInt32 * times = typeid_cast(block.getByPosition(arguments[0]).column.get())) - { - auto res = ColumnUInt32::create(); - ColumnUInt32::Container & res_vec = res->getData(); - const ColumnUInt32::Container & vec = times->getData(); - - size_t size = vec.size(); - res_vec.resize(size); - - for (size_t i = 0; i < size; ++i) - res_vec[i] = vec[i] / TIME_SLOT_SIZE * TIME_SLOT_SIZE; - - block.getByPosition(result).column = std::move(res); - } - else - throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() - + " of argument of function " + getName(), - ErrorCodes::ILLEGAL_COLUMN); - } -}; - - -template -struct TimeSlotsImpl -{ - static void vector_vector( - const PaddedPODArray & starts, const PaddedPODArray & durations, - PaddedPODArray & result_values, ColumnArray::Offsets & result_offsets) - { - size_t size = starts.size(); - - result_offsets.resize(size); - result_values.reserve(size); - - ColumnArray::Offset current_offset = 0; - for (size_t i = 0; i < size; ++i) - { - for (UInt32 value = starts[i] / TIME_SLOT_SIZE; value <= (starts[i] + durations[i]) / TIME_SLOT_SIZE; ++value) - { - result_values.push_back(value * TIME_SLOT_SIZE); - ++current_offset; - } - - result_offsets[i] = current_offset; - } - } - - static void vector_constant( - const PaddedPODArray & starts, DurationType duration, - PaddedPODArray & result_values, ColumnArray::Offsets & result_offsets) - { - size_t size = starts.size(); - - result_offsets.resize(size); - result_values.reserve(size); - - ColumnArray::Offset current_offset = 0; - for (size_t i = 0; i < size; ++i) - { - for (UInt32 value = starts[i] / TIME_SLOT_SIZE; value <= (starts[i] + duration) / TIME_SLOT_SIZE; ++value) - { - result_values.push_back(value * TIME_SLOT_SIZE); - ++current_offset; - } - - result_offsets[i] = current_offset; - } - } - - static void constant_vector( - UInt32 start, const PaddedPODArray & durations, - PaddedPODArray & result_values, ColumnArray::Offsets & result_offsets) - { - size_t size = durations.size(); - - result_offsets.resize(size); - result_values.reserve(size); - - ColumnArray::Offset current_offset = 0; - for (size_t i = 0; i < size; ++i) - { - for (UInt32 value = start / TIME_SLOT_SIZE; value <= (start + durations[i]) / TIME_SLOT_SIZE; ++value) - { - result_values.push_back(value * TIME_SLOT_SIZE); - ++current_offset; - } - - result_offsets[i] = current_offset; - } - } - - static void constant_constant( - UInt32 start, DurationType duration, - Array & result) - { - for (UInt32 value = start / TIME_SLOT_SIZE; value <= (start + duration) / TIME_SLOT_SIZE; ++value) - result.push_back(static_cast(value * TIME_SLOT_SIZE)); - } -}; - - -class FunctionTimeSlots : public IFunction -{ -public: - static constexpr auto name = "timeSlots"; - static FunctionPtr create(const Context &) { return std::make_shared(); } - - String getName() const override - { - return name; - } - - size_t getNumberOfArguments() const override { return 2; } - - DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override - { - if (!WhichDataType(arguments[0]).isDateTime()) - throw Exception("Illegal type " + arguments[0]->getName() + " of first argument of function " + getName() + ". Must be DateTime.", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - if (!WhichDataType(arguments[1]).isUInt32()) - throw Exception("Illegal type " + arguments[1]->getName() + " of second argument of function " + getName() + ". Must be UInt32.", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - return std::make_shared(std::make_shared()); - } - - void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override - { - auto starts = checkAndGetColumn(block.getByPosition(arguments[0]).column.get()); - auto const_starts = checkAndGetColumnConst(block.getByPosition(arguments[0]).column.get()); - - auto durations = checkAndGetColumn(block.getByPosition(arguments[1]).column.get()); - auto const_durations = checkAndGetColumnConst(block.getByPosition(arguments[1]).column.get()); - - auto res = ColumnArray::create(ColumnUInt32::create()); - ColumnUInt32::Container & res_values = typeid_cast(res->getData()).getData(); - - if (starts && durations) - { - TimeSlotsImpl::vector_vector(starts->getData(), durations->getData(), res_values, res->getOffsets()); - block.getByPosition(result).column = std::move(res); - } - else if (starts && const_durations) - { - TimeSlotsImpl::vector_constant(starts->getData(), const_durations->getValue(), res_values, res->getOffsets()); - block.getByPosition(result).column = std::move(res); - } - else if (const_starts && durations) - { - TimeSlotsImpl::constant_vector(const_starts->getValue(), durations->getData(), res_values, res->getOffsets()); - block.getByPosition(result).column = std::move(res); - } - else if (const_starts && const_durations) - { - Array const_res; - TimeSlotsImpl::constant_constant(const_starts->getValue(), const_durations->getValue(), const_res); - block.getByPosition(result).column = block.getByPosition(result).type->createColumnConst(input_rows_count, const_res); - } - else - throw Exception("Illegal columns " + block.getByPosition(arguments[0]).column->getName() - + ", " + block.getByPosition(arguments[1]).column->getName() - + " of arguments of function " + getName(), - ErrorCodes::ILLEGAL_COLUMN); - } -}; - - -/** formatDateTime(time, 'pattern') - * Performs formatting of time, according to provided pattern. - * - * This function is optimized with an assumption, that the resulting strings are fixed width. - * (This assumption is fulfilled for currently supported formatting options). - * - * It is implemented in two steps. - * At first step, it creates a pattern of zeros, literal characters, whitespaces, etc. - * and quickly fills resulting charater array (string column) with this pattern. - * At second step, it walks across the resulting character array and modifies/replaces specific charaters, - * by calling some functions by pointers and shifting cursor by specified amount. - * - * Advantages: - * - memcpy is mostly unrolled; - * - low number of arithmetic ops due to pre-filled pattern; - * - for somewhat reason, function by pointer call is faster than switch/case. - * - * Possible further optimization options: - * - slightly interleave first and second step for better cache locality - * (but it has no sense when character array fits in L1d cache); - * - avoid indirect function calls and inline functions with JIT compilation. - * - * Performance on Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz: - * - * WITH formatDateTime(now() + number, '%H:%M:%S') AS x SELECT count() FROM system.numbers WHERE NOT ignore(x); - * - 97 million rows per second per core; - * - * WITH formatDateTime(toDateTime('2018-01-01 00:00:00') + number, '%F %T') AS x SELECT count() FROM system.numbers WHERE NOT ignore(x) - * - 71 million rows per second per core; - * - * select count() from (select formatDateTime(t, '%m/%d/%Y %H:%M:%S') from (select toDateTime('2018-01-01 00:00:00')+number as t from numbers(100000000))); - * - 53 million rows per second per core; - * - * select count() from (select formatDateTime(t, 'Hello %Y World') from (select toDateTime('2018-01-01 00:00:00')+number as t from numbers(100000000))); - * - 138 million rows per second per core; - * - * PS. We can make this function to return FixedString. Currently it returns String. - */ -class FunctionFormatDateTime : public IFunction -{ -private: - /// Time is either UInt32 for DateTime or UInt16 for Date. - template - class Action - { - public: - using Func = void (*)(char *, Time, const DateLUTImpl &); - - Func func; - size_t shift; - - Action(Func func, size_t shift = 0) : func(func), shift(shift) {} - - void perform(char *& target, Time source, const DateLUTImpl & timezone) - { - func(target, source, timezone); - target += shift; - } - - private: - template - static inline void writeNumber2(char * p, T v) - { - static const char digits[201] = - "00010203040506070809" - "10111213141516171819" - "20212223242526272829" - "30313233343536373839" - "40414243444546474849" - "50515253545556575859" - "60616263646566676869" - "70717273747576777879" - "80818283848586878889" - "90919293949596979899"; - - memcpy(p, &digits[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); - } - - public: - static void noop(char *, Time, const DateLUTImpl &) - { - } - - static void century(char * target, Time source, const DateLUTImpl & timezone) - { - auto year = ToYearImpl::execute(source, timezone); - auto century = year / 100; - writeNumber2(target, century); - } - - static void dayOfMonth(char * target, Time source, const DateLUTImpl & timezone) - { - writeNumber2(target, ToDayOfMonthImpl::execute(source, timezone)); - } - - static void americanDate(char * target, Time source, const DateLUTImpl & timezone) - { - writeNumber2(target, ToMonthImpl::execute(source, timezone)); - writeNumber2(target + 3, ToDayOfMonthImpl::execute(source, timezone)); - writeNumber2(target + 6, ToYearImpl::execute(source, timezone) % 100); - } - - static void dayOfMonthSpacePadded(char * target, Time source, const DateLUTImpl & timezone) - { - auto day = ToDayOfMonthImpl::execute(source, timezone); - if (day < 10) - target[1] += day; - else - writeNumber2(target, day); - } - - static void ISO8601Date(char * target, Time source, const DateLUTImpl & timezone) - { - writeNumber4(target, ToYearImpl::execute(source, timezone)); - writeNumber2(target + 5, ToMonthImpl::execute(source, timezone)); - writeNumber2(target + 8, ToDayOfMonthImpl::execute(source, timezone)); - } - - static void dayOfYear(char * target, Time source, const DateLUTImpl & timezone) - { - writeNumber3(target, ToDayOfYearImpl::execute(source, timezone)); - } - - static void month(char * target, Time source, const DateLUTImpl & timezone) - { - writeNumber2(target, ToMonthImpl::execute(source, timezone)); - } - - static void dayOfWeek(char * target, Time source, const DateLUTImpl & timezone) - { - *target += ToDayOfWeekImpl::execute(source, timezone); - } - - static void dayOfWeek0To6(char * target, Time source, const DateLUTImpl & timezone) - { - auto day = ToDayOfWeekImpl::execute(source, timezone); - *target += (day == 7 ? 0 : day); - } - - static void ISO8601Week(char * target, Time source, const DateLUTImpl & timezone) - { - writeNumber2(target, ToISOWeekImpl::execute(source, timezone)); - } - - static void year2(char * target, Time source, const DateLUTImpl & timezone) - { - writeNumber2(target, ToYearImpl::execute(source, timezone) % 100); - } - - static void year4(char * target, Time source, const DateLUTImpl & timezone) - { - writeNumber4(target, ToYearImpl::execute(source, timezone)); - } - - static void hour24(char * target, Time source, const DateLUTImpl & timezone) - { - writeNumber2(target, ToHourImpl::execute(source, timezone)); - } - - static void hour12(char * target, Time source, const DateLUTImpl & timezone) - { - auto x = ToHourImpl::execute(source, timezone); - writeNumber2(target, x == 0 ? 12 : (x > 12 ? x - 12 : x)); - } - - static void minute(char * target, Time source, const DateLUTImpl & timezone) - { - writeNumber2(target, ToMinuteImpl::execute(source, timezone)); - } - - static void AMPM(char * target, Time source, const DateLUTImpl & timezone) - { - auto hour = ToHourImpl::execute(source, timezone); - if (hour >= 12) - *target = 'P'; - } - - static void hhmm24(char * target, Time source, const DateLUTImpl & timezone) - { - writeNumber2(target, ToHourImpl::execute(source, timezone)); - writeNumber2(target + 3, ToMinuteImpl::execute(source, timezone)); - } - - static void second(char * target, Time source, const DateLUTImpl & timezone) - { - writeNumber2(target, ToSecondImpl::execute(source, timezone)); - } - - static void ISO8601Time(char * target, Time source, const DateLUTImpl & timezone) - { - writeNumber2(target, ToHourImpl::execute(source, timezone)); - writeNumber2(target + 3, ToMinuteImpl::execute(source, timezone)); - writeNumber2(target + 6, ToSecondImpl::execute(source, timezone)); - } - }; - -public: - static constexpr auto name = "formatDateTime"; - - static FunctionPtr create(const Context &) { return std::make_shared(); } - - String getName() const override - { - return name; - } - - bool useDefaultImplementationForConstants() const override { return true; } - - ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1, 2}; } - - 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).isDateOrDateTime()) - throw Exception("Illegal type " + arguments[0].type->getName() + " of 1 argument of function " + getName() + - ". Should be a date or a date with time", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - if (!WhichDataType(arguments[1].type).isString()) - throw Exception("Illegal type " + arguments[1].type->getName() + " of 2 argument of function " + getName() + ". Must be String.", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - if (arguments.size() == 3) - { - if (!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(); - } - - void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override - { - if (!executeType(block, arguments, result) - && !executeType(block, arguments, result)) - throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() - + " of function " + getName() + ", must be Date or DateTime", - ErrorCodes::ILLEGAL_COLUMN); - } - - template - bool executeType(Block & block, const ColumnNumbers & arguments, size_t result) - { - if (auto * times = checkAndGetColumn>(block.getByPosition(arguments[0]).column.get())) - { - const ColumnConst * pattern_column = checkAndGetColumnConst(block.getByPosition(arguments[1]).column.get()); - - if (!pattern_column) - throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName() - + " of second ('format') argument of function " + getName() - + ". Must be constant string.", - ErrorCodes::ILLEGAL_COLUMN); - - String pattern = pattern_column->getValue(); - - std::vector> instructions; - String pattern_to_fill = parsePattern(pattern, instructions); - size_t result_size = pattern_to_fill.size(); - - const DateLUTImpl * time_zone_tmp = nullptr; - if (arguments.size() == 3) - time_zone_tmp = &extractTimeZoneFromFunctionArguments(block, arguments, 2, 0); - else - time_zone_tmp = &DateLUT::instance(); - - const DateLUTImpl & time_zone = *time_zone_tmp; - - const typename ColumnVector::Container & vec = times->getData(); - - auto col_res = ColumnString::create(); - auto & dst_data = col_res->getChars(); - auto & dst_offsets = col_res->getOffsets(); - dst_data.resize(vec.size() * (result_size + 1)); - dst_offsets.resize(vec.size()); - - /// Fill result with literals. - { - UInt8 * begin = dst_data.data(); - UInt8 * end = begin + dst_data.size(); - UInt8 * pos = begin; - - if (pos < end) - { - memcpy(pos, pattern_to_fill.data(), result_size + 1); /// With zero terminator. - pos += result_size + 1; - } - - /// Fill by copying exponential growing ranges. - while (pos < end) - { - size_t bytes_to_copy = std::min(pos - begin, end - pos); - memcpy(pos, begin, bytes_to_copy); - pos += bytes_to_copy; - } - } - - auto begin = reinterpret_cast(dst_data.data()); - auto pos = begin; - - for (size_t i = 0; i < vec.size(); ++i) - { - for(auto & instruction : instructions) - instruction.perform(pos, vec[i], time_zone); - - dst_offsets[i] = pos - begin; - } - - dst_data.resize(pos - begin); - block.getByPosition(result).column = std::move(col_res); - return true; - } - - return false; - } - - template - String parsePattern(const String & pattern, std::vector> & instructions) const - { - String result; - - const char * pos = pattern.data(); - const char * end = pos + pattern.size(); - - /// Add shift to previous action; or if there were none, add noop action with shift. - auto addShift = [&](size_t amount) - { - if (instructions.empty()) - instructions.emplace_back(&Action::noop); - instructions.back().shift += amount; - }; - - /// If the argument was DateTime, add instruction for printing. If it was date, just shift (the buffer is pre-filled with default values). - auto addInstructionOrShift = [&](typename Action::Func func [[maybe_unused]], size_t shift) - { - if constexpr (std::is_same_v) - instructions.emplace_back(func, shift); - else - addShift(shift); - }; - - while (true) - { - const char * percent_pos = find_first_symbols<'%'>(pos, end); - - if (percent_pos < end) - { - if (pos < percent_pos) - { - result.append(pos, percent_pos); - addShift(percent_pos - pos); - } - - pos = percent_pos + 1; - - if (pos >= end) - throw Exception("Sign '%' is the last in pattern, if you need it, use '%%'", ErrorCodes::BAD_ARGUMENTS); - - switch (*pos) - { - // Year, divided by 100, zero-padded - case 'C': - instructions.emplace_back(&Action::century, 2); - result.append("00"); - break; - - // Day of month, zero-padded (01-31) - case 'd': - instructions.emplace_back(&Action::dayOfMonth, 2); - result.append("00"); - break; - - // Short MM/DD/YY date, equivalent to %m/%d/%y - case 'D': - instructions.emplace_back(&Action::americanDate, 8); - result.append("00/00/00"); - break; - - // Day of month, space-padded ( 1-31) 23 - case 'e': - instructions.emplace_back(&Action::dayOfMonthSpacePadded, 2); - result.append(" 0"); - break; - - // Short YYYY-MM-DD date, equivalent to %Y-%m-%d 2001-08-23 - case 'F': - instructions.emplace_back(&Action::ISO8601Date, 10); - result.append("0000-00-00"); - break; - - // Day of the year (001-366) 235 - case 'j': - instructions.emplace_back(&Action::dayOfYear, 3); - result.append("000"); - break; - - // Month as a decimal number (01-12) - case 'm': - instructions.emplace_back(&Action::month, 2); - result.append("00"); - break; - - // ISO 8601 weekday as number with Monday as 1 (1-7) - case 'u': - instructions.emplace_back(&Action::dayOfWeek, 1); - result.append("0"); - break; - - // ISO 8601 week number (01-53) - case 'V': - instructions.emplace_back(&Action::ISO8601Week, 2); - result.append("00"); - break; - - // Weekday as a decimal number with Sunday as 0 (0-6) 4 - case 'w': - instructions.emplace_back(&Action::dayOfWeek0To6, 1); - result.append("0"); - break; - - // Two digits year - case 'y': - instructions.emplace_back(&Action::year2, 2); - result.append("00"); - break; - - // Four digits year - case 'Y': - instructions.emplace_back(&Action::year4, 4); - result.append("0000"); - break; - - /// Time components. If the argument is Date, not a DateTime, then this components will have default value. - - // Minute (00-59) - case 'M': - addInstructionOrShift(&Action::minute, 2); - result.append("00"); - break; - - // AM or PM - case 'p': - addInstructionOrShift(&Action::AMPM, 2); - result.append("AM"); - break; - - // 24-hour HH:MM time, equivalent to %H:%M 14:55 - case 'R': - addInstructionOrShift(&Action::hhmm24, 5); - result.append("00:00"); - break; - - // Seconds - case 'S': - addInstructionOrShift(&Action::second, 2); - result.append("00"); - break; - - // ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S 14:55:02 - case 'T': - addInstructionOrShift(&Action::ISO8601Time, 8); - result.append("00:00:00"); - break; - - // Hour in 24h format (00-23) - case 'H': - addInstructionOrShift(&Action::hour24, 2); - result.append("00"); - break; - - // Hour in 12h format (01-12) - case 'I': - addInstructionOrShift(&Action::hour12, 2); - result.append("12"); - break; - - /// Escaped literal characters. - case '%': - result += '%'; - addShift(1); - break; - case 't': - result += '\t'; - addShift(1); - break; - case 'n': - result += '\n'; - addShift(1); - break; - - // Unimplemented - case 'U': [[fallthrough]]; - case 'W': - throw Exception("Wrong pattern '" + pattern + "', symbol '" + *pos + " is not implemented ' for function " + getName(), - ErrorCodes::NOT_IMPLEMENTED); - - default: - throw Exception( - "Wrong pattern '" + pattern + "', unexpected symbol '" + *pos + "' for function " + getName(), ErrorCodes::ILLEGAL_COLUMN); - } - - ++pos; - } - else - { - result.append(pos, end); - addShift(end + 1 - pos); /// including zero terminator - break; - } - } - - return result; - } -}; - - -using FunctionToYear = FunctionDateOrDateTimeToSomething; -using FunctionToQuarter = FunctionDateOrDateTimeToSomething; -using FunctionToMonth = FunctionDateOrDateTimeToSomething; -using FunctionToDayOfMonth = FunctionDateOrDateTimeToSomething; -using FunctionToDayOfWeek = FunctionDateOrDateTimeToSomething; -using FunctionToDayOfYear = FunctionDateOrDateTimeToSomething; -using FunctionToHour = FunctionDateOrDateTimeToSomething; -using FunctionToMinute = FunctionDateOrDateTimeToSomething; -using FunctionToSecond = FunctionDateOrDateTimeToSomething; -using FunctionToStartOfDay = FunctionDateOrDateTimeToSomething; -using FunctionToMonday = FunctionDateOrDateTimeToSomething; -using FunctionToISOWeek = FunctionDateOrDateTimeToSomething; -using FunctionToISOYear = FunctionDateOrDateTimeToSomething; -using FunctionToStartOfMonth = FunctionDateOrDateTimeToSomething; -using FunctionToStartOfQuarter = FunctionDateOrDateTimeToSomething; -using FunctionToStartOfYear = FunctionDateOrDateTimeToSomething; -using FunctionToStartOfMinute = FunctionDateOrDateTimeToSomething; -using FunctionToStartOfFiveMinute = FunctionDateOrDateTimeToSomething; -using FunctionToStartOfFifteenMinutes = FunctionDateOrDateTimeToSomething; -using FunctionToStartOfHour = FunctionDateOrDateTimeToSomething; -using FunctionToStartOfISOYear = FunctionDateOrDateTimeToSomething; -using FunctionToTime = FunctionDateOrDateTimeToSomething; - -using FunctionToRelativeYearNum = FunctionDateOrDateTimeToSomething; -using FunctionToRelativeQuarterNum = FunctionDateOrDateTimeToSomething; -using FunctionToRelativeMonthNum = FunctionDateOrDateTimeToSomething; -using FunctionToRelativeWeekNum = FunctionDateOrDateTimeToSomething; -using FunctionToRelativeDayNum = FunctionDateOrDateTimeToSomething; -using FunctionToRelativeHourNum = FunctionDateOrDateTimeToSomething; -using FunctionToRelativeMinuteNum = FunctionDateOrDateTimeToSomething; -using FunctionToRelativeSecondNum = FunctionDateOrDateTimeToSomething; - -using FunctionToYYYYMM = FunctionDateOrDateTimeToSomething; -using FunctionToYYYYMMDD = FunctionDateOrDateTimeToSomething; -using FunctionToYYYYMMDDhhmmss = FunctionDateOrDateTimeToSomething; - -using FunctionAddSeconds = FunctionDateOrDateTimeAddInterval; -using FunctionAddMinutes = FunctionDateOrDateTimeAddInterval; -using FunctionAddHours = FunctionDateOrDateTimeAddInterval; -using FunctionAddDays = FunctionDateOrDateTimeAddInterval; -using FunctionAddWeeks = FunctionDateOrDateTimeAddInterval; -using FunctionAddMonths = FunctionDateOrDateTimeAddInterval; -using FunctionAddYears = FunctionDateOrDateTimeAddInterval; - -using FunctionSubtractSeconds = FunctionDateOrDateTimeAddInterval; -using FunctionSubtractMinutes = FunctionDateOrDateTimeAddInterval; -using FunctionSubtractHours = FunctionDateOrDateTimeAddInterval; -using FunctionSubtractDays = FunctionDateOrDateTimeAddInterval; -using FunctionSubtractWeeks = FunctionDateOrDateTimeAddInterval; -using FunctionSubtractMonths = FunctionDateOrDateTimeAddInterval; -using FunctionSubtractYears = FunctionDateOrDateTimeAddInterval; - -} diff --git a/dbms/src/Functions/registerFunctionsDateTime.cpp b/dbms/src/Functions/registerFunctionsDateTime.cpp new file mode 100644 index 00000000000..a48c1e445cf --- /dev/null +++ b/dbms/src/Functions/registerFunctionsDateTime.cpp @@ -0,0 +1,123 @@ +#include + + +namespace DB +{ + +void registerFunctionToYear(FunctionFactory &); +void registerFunctionToQuarter(FunctionFactory &); +void registerFunctionToMonth(FunctionFactory &); +void registerFunctionToDayOfMonth(FunctionFactory &); +void registerFunctionToDayOfWeek(FunctionFactory &); +void registerFunctionToDayOfYear(FunctionFactory &); +void registerFunctionToHour(FunctionFactory &); +void registerFunctionToMinute(FunctionFactory &); +void registerFunctionToSecond(FunctionFactory &); +void registerFunctionToStartOfDay(FunctionFactory &); +void registerFunctionToMonday(FunctionFactory &); +void registerFunctionToISOWeek(FunctionFactory &); +void registerFunctionToISOYear(FunctionFactory &); +void registerFunctionToStartOfMonth(FunctionFactory &); +void registerFunctionToStartOfQuarter(FunctionFactory &); +void registerFunctionToStartOfYear(FunctionFactory &); +void registerFunctionToStartOfMinute(FunctionFactory &); +void registerFunctionToStartOfFiveMinute(FunctionFactory &); +void registerFunctionToStartOfFifteenMinutes(FunctionFactory &); +void registerFunctionToStartOfHour(FunctionFactory &); +void registerFunctionToStartOfISOYear(FunctionFactory &); +void registerFunctionToRelativeYearNum(FunctionFactory &); +void registerFunctionToRelativeQuarterNum(FunctionFactory &); +void registerFunctionToRelativeMonthNum(FunctionFactory &); +void registerFunctionToRelativeWeekNum(FunctionFactory &); +void registerFunctionToRelativeDayNum(FunctionFactory &); +void registerFunctionToRelativeHourNum(FunctionFactory &); +void registerFunctionToRelativeMinuteNum(FunctionFactory &); +void registerFunctionToRelativeSecondNum(FunctionFactory &); +void registerFunctionToTime(FunctionFactory &); +void registerFunctionNow(FunctionFactory &); +void registerFunctionToday(FunctionFactory &); +void registerFunctionYesterday(FunctionFactory &); +void registerFunctionTimeSlot(FunctionFactory &); +void registerFunctionTimeSlots(FunctionFactory &); +void registerFunctionToYYYYMM(FunctionFactory &); +void registerFunctionToYYYYMMDD(FunctionFactory &); +void registerFunctionToYYYYMMDDhhmmss(FunctionFactory &); +void registerFunctionAddSeconds(FunctionFactory &); +void registerFunctionAddMinutes(FunctionFactory &); +void registerFunctionAddHours(FunctionFactory &); +void registerFunctionAddDays(FunctionFactory &); +void registerFunctionAddWeeks(FunctionFactory &); +void registerFunctionAddMonths(FunctionFactory &); +void registerFunctionAddYears(FunctionFactory &); +void registerFunctionSubtractSeconds(FunctionFactory &); +void registerFunctionSubtractMinutes(FunctionFactory &); +void registerFunctionSubtractHours(FunctionFactory &); +void registerFunctionSubtractDays(FunctionFactory &); +void registerFunctionSubtractWeeks(FunctionFactory &); +void registerFunctionSubtractMonths(FunctionFactory &); +void registerFunctionSubtractYears(FunctionFactory &); +void registerFunctionDateDiff(FunctionFactory &); +void registerFunctionToTimeZone(FunctionFactory &); +void registerFunctionFormatDateTime(FunctionFactory &); + + +void registerFunctionsDateTime(FunctionFactory & factory) +{ + registerFunctionToYear(factory); + registerFunctionToQuarter(factory); + registerFunctionToMonth(factory); + registerFunctionToDayOfMonth(factory); + registerFunctionToDayOfWeek(factory); + registerFunctionToDayOfYear(factory); + registerFunctionToHour(factory); + registerFunctionToMinute(factory); + registerFunctionToSecond(factory); + registerFunctionToStartOfDay(factory); + registerFunctionToMonday(factory); + registerFunctionToISOWeek(factory); + registerFunctionToISOYear(factory); + registerFunctionToStartOfMonth(factory); + registerFunctionToStartOfQuarter(factory); + registerFunctionToStartOfYear(factory); + registerFunctionToStartOfMinute(factory); + registerFunctionToStartOfFiveMinute(factory); + registerFunctionToStartOfFifteenMinutes(factory); + registerFunctionToStartOfHour(factory); + registerFunctionToStartOfISOYear(factory); + registerFunctionToRelativeYearNum(factory); + registerFunctionToRelativeQuarterNum(factory); + registerFunctionToRelativeMonthNum(factory); + registerFunctionToRelativeWeekNum(factory); + registerFunctionToRelativeDayNum(factory); + registerFunctionToRelativeHourNum(factory); + registerFunctionToRelativeMinuteNum(factory); + registerFunctionToRelativeSecondNum(factory); + registerFunctionToTime(factory); + registerFunctionNow(factory); + registerFunctionToday(factory); + registerFunctionYesterday(factory); + registerFunctionTimeSlot(factory); + registerFunctionTimeSlots(factory); + registerFunctionToYYYYMM(factory); + registerFunctionToYYYYMMDD(factory); + registerFunctionToYYYYMMDDhhmmss(factory); + registerFunctionAddSeconds(factory); + registerFunctionAddMinutes(factory); + registerFunctionAddHours(factory); + registerFunctionAddDays(factory); + registerFunctionAddWeeks(factory); + registerFunctionAddMonths(factory); + registerFunctionAddYears(factory); + registerFunctionSubtractSeconds(factory); + registerFunctionSubtractMinutes(factory); + registerFunctionSubtractHours(factory); + registerFunctionSubtractDays(factory); + registerFunctionSubtractWeeks(factory); + registerFunctionSubtractMonths(factory); + registerFunctionSubtractYears(factory); + registerFunctionDateDiff(factory); + registerFunctionToTimeZone(factory); + registerFunctionFormatDateTime(factory); +} + +}