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
23namespace DB
24{
25
26DataTypeDateTime64::DataTypeDateTime64(UInt32 scale_, const std::string & time_zone_name)
27 : DataTypeDecimalBase<DateTime64>(DecimalUtils::maxPrecision<DateTime64>(), scale_),
28 TimezoneMixin(time_zone_name)
29{
30}
31
32DataTypeDateTime64::DataTypeDateTime64(UInt32 scale_, const TimezoneMixin & time_zone_info)
33 : DataTypeDecimalBase<DateTime64>(DecimalUtils::maxPrecision<DateTime64>() - scale_, scale_),
34 TimezoneMixin(time_zone_info)
35{}
36
37std::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
47void 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
52void 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
59void DataTypeDateTime64::deserializeWholeText(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const
60{
61 deserializeTextEscaped(column, istr, settings);
62}
63
64void 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
69static 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
82void 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
89void 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
96void 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
111void 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
118void 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
133void 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
140void 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
160void 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
167void 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
184bool 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
191namespace ErrorCodes
192{
193 extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
194 extern const int ILLEGAL_TYPE_OF_ARGUMENT;
195}
196
197enum class ArgumentKind
198{
199 Optional,
200 Mandatory
201};
202
203template <typename T, ArgumentKind Kind>
204std::conditional_t<Kind == ArgumentKind::Optional, std::optional<T>, T>
205getArgument(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
236static 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
247void registerDataTypeDateTime64(DataTypeFactory & factory)
248{
249 factory.registerDataType("DateTime64", create64, DataTypeFactory::CaseInsensitive);
250}
251
252}
253