1#pragma once
2#include <regex>
3#include <Columns/ColumnVector.h>
4#include <Columns/ColumnsNumber.h>
5#include <Core/Types.h>
6#include <Core/DecimalFunctions.h>
7#include <Functions/FunctionHelpers.h>
8#include <Functions/extractTimeZoneFromFunctionArguments.h>
9#include <Common/Exception.h>
10#include <common/DateLUTImpl.h>
11
12/// The default mode value to use for the WEEK() function
13#define DEFAULT_WEEK_MODE 0
14
15namespace DB
16{
17namespace ErrorCodes
18{
19 extern const int ILLEGAL_TYPE_OF_ARGUMENT;
20 extern const int ILLEGAL_COLUMN;
21}
22
23/**
24 * CustomWeek Transformations.
25 */
26
27static inline UInt32 dateIsNotSupported(const char * name)
28{
29 throw Exception("Illegal type Date of argument for function " + std::string(name), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
30}
31
32/// This factor transformation will say that the function is monotone everywhere.
33struct ZeroTransform
34{
35 static inline UInt16 execute(UInt32, UInt8, const DateLUTImpl &) { return 0; }
36 static inline UInt16 execute(UInt16, UInt8, const DateLUTImpl &) { return 0; }
37};
38
39struct ToWeekImpl
40{
41 static constexpr auto name = "toWeek";
42
43 static inline UInt8 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone)
44 {
45 YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode);
46 return yw.second;
47 }
48 static inline UInt8 execute(UInt16 d, UInt8 week_mode, const DateLUTImpl & time_zone)
49 {
50 YearWeek yw = time_zone.toYearWeek(DayNum(d), week_mode);
51 return yw.second;
52 }
53
54 using FactorTransform = ZeroTransform;
55};
56
57struct ToYearWeekImpl
58{
59 static constexpr auto name = "toYearWeek";
60
61 static inline UInt32 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone)
62 {
63 YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode | static_cast<UInt32>(WeekModeFlag::YEAR));
64 return yw.first * 100 + yw.second;
65 }
66 static inline UInt32 execute(UInt16 d, UInt8 week_mode, const DateLUTImpl & time_zone)
67 {
68 YearWeek yw = time_zone.toYearWeek(DayNum(d), week_mode | static_cast<UInt32>(WeekModeFlag::YEAR));
69 return yw.first * 100 + yw.second;
70 }
71
72 using FactorTransform = ZeroTransform;
73};
74
75struct ToStartOfWeekImpl
76{
77 static constexpr auto name = "toStartOfWeek";
78
79 static inline UInt16 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone)
80 {
81 return time_zone.toFirstDayNumOfWeek(time_zone.toDayNum(t), week_mode);
82 }
83 static inline UInt16 execute(UInt16 d, UInt8 week_mode, const DateLUTImpl & time_zone)
84 {
85 return time_zone.toFirstDayNumOfWeek(DayNum(d), week_mode);
86 }
87
88 using FactorTransform = ZeroTransform;
89};
90
91template <typename FromType, typename ToType, typename Transform>
92struct Transformer
93{
94 Transformer(Transform transform_)
95 : transform(std::move(transform_))
96 {}
97
98 template <typename FromVectorType, typename ToVectorType>
99 void
100 vector(const FromVectorType & vec_from, ToVectorType & vec_to, UInt8 week_mode, const DateLUTImpl & time_zone) const
101 {
102 size_t size = vec_from.size();
103 vec_to.resize(size);
104
105 for (size_t i = 0; i < size; ++i)
106 vec_to[i] = transform.execute(vec_from[i], week_mode, time_zone);
107 }
108
109private:
110 const Transform transform;
111};
112
113
114template <typename FromDataType, typename ToDataType>
115struct CustomWeekTransformImpl
116{
117 template <typename Transform>
118 static void execute(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/, Transform transform = {})
119 {
120 const auto op = Transformer<typename FromDataType::FieldType, typename ToDataType::FieldType, Transform>{std::move(transform)};
121
122 UInt8 week_mode = DEFAULT_WEEK_MODE;
123 if (arguments.size() > 1)
124 {
125 if (const auto week_mode_column = checkAndGetColumnConst<ColumnUInt8>(block.getByPosition(arguments[1]).column.get()))
126 week_mode = week_mode_column->getValue<UInt8>();
127 }
128
129 const DateLUTImpl & time_zone = extractTimeZoneFromFunctionArguments(block, arguments, 2, 0);
130 const ColumnPtr source_col = block.getByPosition(arguments[0]).column;
131 if (const auto * sources = checkAndGetColumn<typename FromDataType::ColumnType>(source_col.get()))
132 {
133 auto col_to = ToDataType::ColumnType::create();
134 op.vector(sources->getData(), col_to->getData(), week_mode, time_zone);
135 block.getByPosition(result).column = std::move(col_to);
136 }
137 else
138 {
139 throw Exception(
140 "Illegal column " + block.getByPosition(arguments[0]).column->getName() + " of first argument of function "
141 + Transform::name,
142 ErrorCodes::ILLEGAL_COLUMN);
143 }
144 }
145};
146
147}
148