1 | #include <DataTypes/DataTypeDateTime64.h> |
2 | |
3 | #include <Columns/ColumnDecimal.h> |
4 | #include <Columns/ColumnVector.h> |
5 | #include <Columns/ColumnsNumber.h> |
6 | #include <Common/assert_cast.h> |
7 | #include <Common/typeid_cast.h> |
8 | #include <common/DateLUT.h> |
9 | #include <DataTypes/DataTypeFactory.h> |
10 | #include <Formats/FormatSettings.h> |
11 | #include <Formats/ProtobufReader.h> |
12 | #include <Formats/ProtobufWriter.h> |
13 | #include <IO/Operators.h> |
14 | #include <IO/ReadHelpers.h> |
15 | #include <IO/WriteBufferFromString.h> |
16 | #include <IO/WriteHelpers.h> |
17 | #include <IO/parseDateTimeBestEffort.h> |
18 | #include <Parsers/ASTLiteral.h> |
19 | |
20 | #include <optional> |
21 | #include <string> |
22 | |
23 | namespace DB |
24 | { |
25 | |
26 | DataTypeDateTime64::DataTypeDateTime64(UInt32 scale_, const std::string & time_zone_name) |
27 | : DataTypeDecimalBase<DateTime64>(DecimalUtils::maxPrecision<DateTime64>(), scale_), |
28 | TimezoneMixin(time_zone_name) |
29 | { |
30 | } |
31 | |
32 | DataTypeDateTime64::DataTypeDateTime64(UInt32 scale_, const TimezoneMixin & time_zone_info) |
33 | : DataTypeDecimalBase<DateTime64>(DecimalUtils::maxPrecision<DateTime64>() - scale_, scale_), |
34 | TimezoneMixin(time_zone_info) |
35 | {} |
36 | |
37 | std::string DataTypeDateTime64::doGetName() const |
38 | { |
39 | if (!has_explicit_time_zone) |
40 | return std::string(getFamilyName()) + "(" + std::to_string(this->scale) + ")" ; |
41 | |
42 | WriteBufferFromOwnString out; |
43 | out << "DateTime64(" << this->scale << ", " << quote << time_zone.getTimeZone() << ")" ; |
44 | return out.str(); |
45 | } |
46 | |
47 | void DataTypeDateTime64::serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & /*settings*/) const |
48 | { |
49 | writeDateTimeText(assert_cast<const ColumnType &>(column).getData()[row_num], scale, ostr, time_zone); |
50 | } |
51 | |
52 | void DataTypeDateTime64::deserializeText(IColumn & column, ReadBuffer & istr, const FormatSettings &) const |
53 | { |
54 | DateTime64 result = 0; |
55 | readDateTime64Text(result, this->getScale(), istr, time_zone); |
56 | assert_cast<ColumnType &>(column).getData().push_back(result); |
57 | } |
58 | |
59 | void DataTypeDateTime64::deserializeWholeText(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const |
60 | { |
61 | deserializeTextEscaped(column, istr, settings); |
62 | } |
63 | |
64 | void DataTypeDateTime64::serializeTextEscaped(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const |
65 | { |
66 | serializeText(column, row_num, ostr, settings); |
67 | } |
68 | |
69 | static inline void readText(DateTime64 & x, UInt32 scale, ReadBuffer & istr, const FormatSettings & settings, const DateLUTImpl & time_zone, const DateLUTImpl & utc_time_zone) |
70 | { |
71 | switch (settings.date_time_input_format) |
72 | { |
73 | case FormatSettings::DateTimeInputFormat::Basic: |
74 | readDateTime64Text(x, scale, istr, time_zone); |
75 | return; |
76 | case FormatSettings::DateTimeInputFormat::BestEffort: |
77 | parseDateTime64BestEffort(x, scale, istr, time_zone, utc_time_zone); |
78 | return; |
79 | } |
80 | } |
81 | |
82 | void DataTypeDateTime64::deserializeTextEscaped(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const |
83 | { |
84 | DateTime64 x = 0; |
85 | readText(x, scale, istr, settings, time_zone, utc_time_zone); |
86 | assert_cast<ColumnType &>(column).getData().push_back(x); |
87 | } |
88 | |
89 | void DataTypeDateTime64::serializeTextQuoted(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const |
90 | { |
91 | writeChar('\'', ostr); |
92 | serializeText(column, row_num, ostr, settings); |
93 | writeChar('\'', ostr); |
94 | } |
95 | |
96 | void DataTypeDateTime64::deserializeTextQuoted(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const |
97 | { |
98 | DateTime64 x = 0; |
99 | if (checkChar('\'', istr)) /// Cases: '2017-08-31 18:36:48' or '1504193808' |
100 | { |
101 | readText(x, scale, istr, settings, time_zone, utc_time_zone); |
102 | assertChar('\'', istr); |
103 | } |
104 | else /// Just 1504193808 or 01504193808 |
105 | { |
106 | readIntText(x, istr); |
107 | } |
108 | assert_cast<ColumnType &>(column).getData().push_back(x); /// It's important to do this at the end - for exception safety. |
109 | } |
110 | |
111 | void DataTypeDateTime64::serializeTextJSON(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const |
112 | { |
113 | writeChar('"', ostr); |
114 | serializeText(column, row_num, ostr, settings); |
115 | writeChar('"', ostr); |
116 | } |
117 | |
118 | void DataTypeDateTime64::deserializeTextJSON(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const |
119 | { |
120 | DateTime64 x = 0; |
121 | if (checkChar('"', istr)) |
122 | { |
123 | readText(x, scale, istr, settings, time_zone, utc_time_zone); |
124 | assertChar('"', istr); |
125 | } |
126 | else |
127 | { |
128 | readIntText(x, istr); |
129 | } |
130 | assert_cast<ColumnType &>(column).getData().push_back(x); |
131 | } |
132 | |
133 | void DataTypeDateTime64::serializeTextCSV(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const |
134 | { |
135 | writeChar('"', ostr); |
136 | serializeText(column, row_num, ostr, settings); |
137 | writeChar('"', ostr); |
138 | } |
139 | |
140 | void DataTypeDateTime64::deserializeTextCSV(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const |
141 | { |
142 | DateTime64 x = 0; |
143 | |
144 | if (istr.eof()) |
145 | throwReadAfterEOF(); |
146 | |
147 | char maybe_quote = *istr.position(); |
148 | |
149 | if (maybe_quote == '\'' || maybe_quote == '\"') |
150 | ++istr.position(); |
151 | |
152 | readText(x, scale, istr, settings, time_zone, utc_time_zone); |
153 | |
154 | if (maybe_quote == '\'' || maybe_quote == '\"') |
155 | assertChar(maybe_quote, istr); |
156 | |
157 | assert_cast<ColumnType &>(column).getData().push_back(x); |
158 | } |
159 | |
160 | void DataTypeDateTime64::serializeProtobuf(const IColumn & column, size_t row_num, ProtobufWriter & protobuf, size_t & value_index) const |
161 | { |
162 | if (value_index) |
163 | return; |
164 | value_index = static_cast<bool>(protobuf.writeDateTime64(assert_cast<const ColumnType &>(column).getData()[row_num], scale)); |
165 | } |
166 | |
167 | void DataTypeDateTime64::deserializeProtobuf(IColumn & column, ProtobufReader & protobuf, bool allow_add_row, bool & row_added) const |
168 | { |
169 | row_added = false; |
170 | DateTime64 t = 0; |
171 | if (!protobuf.readDateTime64(t, scale)) |
172 | return; |
173 | |
174 | auto & container = assert_cast<ColumnType &>(column).getData(); |
175 | if (allow_add_row) |
176 | { |
177 | container.emplace_back(t); |
178 | row_added = true; |
179 | } |
180 | else |
181 | container.back() = t; |
182 | } |
183 | |
184 | bool DataTypeDateTime64::equals(const IDataType & rhs) const |
185 | { |
186 | if (auto * ptype = typeid_cast<const DataTypeDateTime64 *>(&rhs)) |
187 | return this->scale == ptype->getScale(); |
188 | return false; |
189 | } |
190 | |
191 | namespace ErrorCodes |
192 | { |
193 | extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; |
194 | extern const int ILLEGAL_TYPE_OF_ARGUMENT; |
195 | } |
196 | |
197 | enum class ArgumentKind |
198 | { |
199 | Optional, |
200 | Mandatory |
201 | }; |
202 | |
203 | template <typename T, ArgumentKind Kind> |
204 | std::conditional_t<Kind == ArgumentKind::Optional, std::optional<T>, T> |
205 | getArgument(const ASTPtr & arguments, size_t argument_index, const char * argument_name, const std::string context_data_type_name) |
206 | { |
207 | using NearestResultType = NearestFieldType<T>; |
208 | const auto fieldType = Field::TypeToEnum<NearestResultType>::value; |
209 | const ASTLiteral * argument = nullptr; |
210 | |
211 | auto exceptionMessage = [=](const String & message) |
212 | { |
213 | return std::string("Parameter #" ) + std::to_string(argument_index) + " '" |
214 | + argument_name + "' for " + context_data_type_name |
215 | + message |
216 | + ", expected: " + Field::Types::toString(fieldType) + " literal." ; |
217 | }; |
218 | |
219 | if (!arguments || arguments->children.size() <= argument_index |
220 | || !(argument = arguments->children[argument_index]->as<ASTLiteral>())) |
221 | { |
222 | if constexpr (Kind == ArgumentKind::Optional) |
223 | return {}; |
224 | else |
225 | throw Exception(exceptionMessage(" is missing" ), |
226 | ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); |
227 | } |
228 | |
229 | if (argument->value.getType() != fieldType) |
230 | throw Exception(exceptionMessage(String(" has wrong type: " ) + argument->value.getTypeName()), |
231 | ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
232 | |
233 | return argument->value.get<NearestResultType>(); |
234 | } |
235 | |
236 | static DataTypePtr create64(const String & /*type_name*/, const ASTPtr & arguments) |
237 | { |
238 | if (!arguments || arguments->size() == 0) |
239 | return std::make_shared<DataTypeDateTime64>(DataTypeDateTime64::default_scale); |
240 | |
241 | const auto scale = getArgument<UInt64, ArgumentKind::Optional>(arguments, 0, "scale" , "DateType64" ); |
242 | const auto timezone = getArgument<String, ArgumentKind::Optional>(arguments, !!scale, "timezone" , "DateType64" ); |
243 | |
244 | return std::make_shared<DataTypeDateTime64>(scale.value_or(DataTypeDateTime64::default_scale), timezone.value_or(String{})); |
245 | } |
246 | |
247 | void registerDataTypeDateTime64(DataTypeFactory & factory) |
248 | { |
249 | factory.registerDataType("DateTime64" , create64, DataTypeFactory::CaseInsensitive); |
250 | } |
251 | |
252 | } |
253 | |