ClickHouse
165 строк · 6.7 Кб
1#include <Columns/ColumnConst.h>
2#include <Columns/ColumnString.h>
3#include <Columns/ColumnsNumber.h>
4#include <DataTypes/DataTypeDate.h>
5#include <DataTypes/DataTypeDateTime.h>
6#include <DataTypes/DataTypeInterval.h>
7#include <Formats/FormatSettings.h>
8#include <Functions/DateTimeTransforms.h>
9#include <Functions/FunctionFactory.h>
10
11
12namespace DB
13{
14namespace ErrorCodes
15{
16extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
17extern const int ILLEGAL_TYPE_OF_ARGUMENT;
18extern const int BAD_ARGUMENTS;
19}
20
21namespace
22{
23
24class FunctionDateTrunc : public IFunction
25{
26public:
27static constexpr auto name = "dateTrunc";
28
29explicit FunctionDateTrunc(ContextPtr context_) : context(context_) {}
30
31static FunctionPtr create(ContextPtr context) { return std::make_shared<FunctionDateTrunc>(context); }
32
33String getName() const override { return name; }
34
35bool isVariadic() const override { return true; }
36bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
37size_t getNumberOfArguments() const override { return 0; }
38
39DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
40{
41/// The first argument is a constant string with the name of datepart.
42
43auto result_type_is_date = false;
44String datepart_param;
45auto check_first_argument = [&] {
46const ColumnConst * datepart_column = checkAndGetColumnConst<ColumnString>(arguments[0].column.get());
47if (!datepart_column)
48throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "First argument for function {} must be constant string: "
49"name of datepart", getName());
50
51datepart_param = Poco::toLower(datepart_column->getValue<String>());
52if (datepart_param.empty())
53throw Exception(ErrorCodes::BAD_ARGUMENTS, "First argument (name of datepart) for function {} cannot be empty",
54getName());
55
56if (!IntervalKind::tryParseString(datepart_param, datepart_kind))
57throw Exception(ErrorCodes::BAD_ARGUMENTS, "{} doesn't look like datepart name in {}", datepart_param, getName());
58
59if (datepart_kind == IntervalKind::Kind::Nanosecond || datepart_kind == IntervalKind::Kind::Microsecond
60|| datepart_kind == IntervalKind::Kind::Millisecond)
61throw Exception(ErrorCodes::BAD_ARGUMENTS, "{} doesn't support {}", getName(), datepart_param);
62
63result_type_is_date = (datepart_kind == IntervalKind::Kind::Year)
64|| (datepart_kind == IntervalKind::Kind::Quarter) || (datepart_kind == IntervalKind::Kind::Month)
65|| (datepart_kind == IntervalKind::Kind::Week);
66};
67
68bool second_argument_is_date = false;
69auto check_second_argument = [&] {
70if (!isDate(arguments[1].type) && !isDateTime(arguments[1].type) && !isDateTime64(arguments[1].type))
71throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of 2nd argument of function {}. "
72"Should be a date or a date with time", arguments[1].type->getName(), getName());
73
74second_argument_is_date = isDate(arguments[1].type);
75
76if (second_argument_is_date && ((datepart_kind == IntervalKind::Kind::Hour)
77|| (datepart_kind == IntervalKind::Kind::Minute) || (datepart_kind == IntervalKind::Kind::Second)))
78throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type Date of argument for function {}", getName());
79};
80
81auto check_timezone_argument = [&] {
82if (!WhichDataType(arguments[2].type).isString())
83throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument of function {}. "
84"This argument is optional and must be a constant string with timezone name",
85arguments[2].type->getName(), getName());
86
87if (second_argument_is_date && result_type_is_date)
88throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
89"The timezone argument of function {} with datepart '{}' "
90"is allowed only when the 2nd argument has the type DateTime",
91getName(), datepart_param);
92};
93
94if (arguments.size() == 2)
95{
96check_first_argument();
97check_second_argument();
98}
99else if (arguments.size() == 3)
100{
101check_first_argument();
102check_second_argument();
103check_timezone_argument();
104}
105else
106{
107throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH,
108"Number of arguments for function {} doesn't match: passed {}, should be 2 or 3",
109getName(), arguments.size());
110}
111
112if (result_type_is_date)
113return std::make_shared<DataTypeDate>();
114else
115return std::make_shared<DataTypeDateTime>(extractTimeZoneNameFromFunctionArguments(arguments, 2, 1, false));
116}
117
118bool useDefaultImplementationForConstants() const override { return true; }
119ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {0, 2}; }
120
121ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override
122{
123ColumnsWithTypeAndName temp_columns(arguments.size());
124temp_columns[0] = arguments[1];
125
126const UInt16 interval_value = 1;
127const ColumnPtr interval_column = ColumnConst::create(ColumnInt64::create(1, interval_value), input_rows_count);
128temp_columns[1] = {interval_column, std::make_shared<DataTypeInterval>(datepart_kind), ""};
129
130auto to_start_of_interval = FunctionFactory::instance().get("toStartOfInterval", context);
131
132if (arguments.size() == 2)
133return to_start_of_interval->build(temp_columns)->execute(temp_columns, result_type, input_rows_count);
134
135temp_columns[2] = arguments[2];
136return to_start_of_interval->build(temp_columns)->execute(temp_columns, result_type, input_rows_count);
137}
138
139bool hasInformationAboutMonotonicity() const override
140{
141return true;
142}
143
144Monotonicity getMonotonicityForRange(const IDataType &, const Field &, const Field &) const override
145{
146return { .is_monotonic = true, .is_always_monotonic = true };
147}
148
149private:
150ContextPtr context;
151mutable IntervalKind::Kind datepart_kind = IntervalKind::Kind::Second;
152};
153
154}
155
156
157REGISTER_FUNCTION(DateTrunc)
158{
159factory.registerFunction<FunctionDateTrunc>();
160
161/// Compatibility alias.
162factory.registerAlias("DATE_TRUNC", "dateTrunc", FunctionFactory::CaseInsensitive);
163}
164
165}
166