Skip to content

Commit 0a4f3a9

Browse files
authored
[fix](datetime) Replace legacy from_date_str with cast function (#61682)
and split individual `CastToTimestampTz` class and Replace `IS_STRICT` and `IS_DATETIME` boolean template parameters with strongly-typed enums `DatelikeParseMode` and `DatelikeTargetType` across all datetime cast operations. This improves code readability and type safety by making template parameters self-documenting.
1 parent 92c09be commit 0a4f3a9

46 files changed

Lines changed: 1347 additions & 423 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

be/src/core/data_type/data_type_date.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "core/string_buffer.hpp"
3333
#include "core/types.h"
3434
#include "core/value/vdatetime_value.h"
35+
#include "exprs/function/cast/cast_to_date_or_datetime_impl.hpp"
3536
#include "util/io_helper.h"
3637

3738
namespace doris {
@@ -47,4 +48,19 @@ MutableColumnPtr DataTypeDate::create_column() const {
4748
return DataTypeNumberBase<PrimitiveType::TYPE_DATE>::create_column();
4849
}
4950

51+
Field DataTypeDate::get_field(const TExprNode& node) const {
52+
VecDateTimeValue value;
53+
CastParameters params;
54+
if (CastToDateOrDatetime::from_string_strict_mode<DatelikeParseMode::STRICT,
55+
DatelikeTargetType::DATE>(
56+
{node.date_literal.value.c_str(), node.date_literal.value.size()}, value, nullptr,
57+
params)) {
58+
value.cast_to_date();
59+
return Field::create_field<TYPE_DATE>(std::move(value));
60+
} else {
61+
throw doris::Exception(doris::ErrorCode::INVALID_ARGUMENT,
62+
"Invalid value: {} for type Date", node.date_literal.value);
63+
}
64+
}
65+
5066
} // namespace doris

be/src/core/data_type/data_type_date.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,7 @@ class DataTypeDate final : public DataTypeNumberBase<PrimitiveType::TYPE_DATE> {
6060
}
6161
#endif
6262
static void cast_to_date(VecDateTimeValue& x);
63-
Field get_field(const TExprNode& node) const override {
64-
VecDateTimeValue value;
65-
if (value.from_date_str(node.date_literal.value.c_str(), node.date_literal.value.size())) {
66-
value.cast_to_date();
67-
return Field::create_field<TYPE_DATE>(std::move(value));
68-
} else {
69-
throw doris::Exception(doris::ErrorCode::INVALID_ARGUMENT,
70-
"Invalid value: {} for type Date", node.date_literal.value);
71-
}
72-
}
63+
Field get_field(const TExprNode& node) const override;
7364

7465
MutableColumnPtr create_column() const override;
7566

be/src/core/data_type/data_type_date_or_datetime_v2.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
#include "core/string_buffer.hpp"
3333
#include "core/types.h"
3434
#include "core/value/vdatetime_value.h"
35+
#include "exprs/function/cast/cast_to_datetimev2_impl.hpp"
36+
#include "exprs/function/cast/cast_to_datev2_impl.hpp"
3537
#include "exprs/function/cast/cast_to_string.h"
3638
#include "util/io_helper.h"
3739

@@ -50,6 +52,35 @@ class IColumn;
5052
#endif
5153

5254
namespace doris {
55+
56+
Field DataTypeDateV2::get_field(const TExprNode& node) const {
57+
DateV2Value<DateV2ValueType> value;
58+
CastParameters params;
59+
if (CastToDateV2::from_string_strict_mode<DatelikeParseMode::STRICT>(
60+
{node.date_literal.value.c_str(), node.date_literal.value.size()}, value, nullptr,
61+
params)) {
62+
return Field::create_field<TYPE_DATEV2>(std::move(value));
63+
} else {
64+
throw doris::Exception(doris::ErrorCode::INVALID_ARGUMENT,
65+
"Invalid value: {} for type DateV2", node.date_literal.value);
66+
}
67+
}
68+
69+
Field DataTypeDateTimeV2::get_field(const TExprNode& node) const {
70+
DateV2Value<DateTimeV2ValueType> value;
71+
const int32_t scale = node.type.types.empty() ? -1 : node.type.types.front().scalar_type.scale;
72+
CastParameters params;
73+
if (CastToDatetimeV2::from_string_strict_mode<DatelikeParseMode::STRICT>(
74+
{node.date_literal.value.c_str(), node.date_literal.value.size()}, value, nullptr,
75+
scale, params)) {
76+
return Field::create_field<TYPE_DATETIMEV2>(std::move(value));
77+
} else {
78+
throw doris::Exception(doris::ErrorCode::INVALID_ARGUMENT,
79+
"Invalid value: {} for type DateTimeV2({})", node.date_literal.value,
80+
_scale);
81+
}
82+
}
83+
5384
bool DataTypeDateV2::equals(const IDataType& rhs) const {
5485
return typeid(rhs) == typeid(*this);
5586
}

be/src/core/data_type/data_type_date_or_datetime_v2.h

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,7 @@ class DataTypeDateV2 final : public DataTypeNumberBase<PrimitiveType::TYPE_DATEV
6666
return std::make_shared<SerDeType>(nesting_level);
6767
}
6868

69-
Field get_field(const TExprNode& node) const override {
70-
DateV2Value<DateV2ValueType> value;
71-
if (value.from_date_str(node.date_literal.value.c_str(),
72-
cast_set<Int32>(node.date_literal.value.size()))) {
73-
return Field::create_field<TYPE_DATEV2>(std::move(value));
74-
} else {
75-
throw doris::Exception(doris::ErrorCode::INVALID_ARGUMENT,
76-
"Invalid value: {} for type DateV2", node.date_literal.value);
77-
}
78-
}
69+
Field get_field(const TExprNode& node) const override;
7970
bool equals(const IDataType& rhs) const override;
8071

8172
#ifdef BE_TEST
@@ -147,19 +138,7 @@ class DataTypeDateTimeV2 final : public DataTypeNumberBase<PrimitiveType::TYPE_D
147138
return std::make_shared<SerDeType>(_scale, nesting_level);
148139
};
149140

150-
Field get_field(const TExprNode& node) const override {
151-
DateV2Value<DateTimeV2ValueType> value;
152-
const int32_t scale =
153-
node.type.types.empty() ? -1 : node.type.types.front().scalar_type.scale;
154-
if (value.from_date_str(node.date_literal.value.c_str(),
155-
cast_set<int32_t>(node.date_literal.value.size()), scale)) {
156-
return Field::create_field<TYPE_DATETIMEV2>(std::move(value));
157-
} else {
158-
throw doris::Exception(doris::ErrorCode::INVALID_ARGUMENT,
159-
"Invalid value: {} for type DateTimeV2({})",
160-
node.date_literal.value, _scale);
161-
}
162-
}
141+
Field get_field(const TExprNode& node) const override;
163142
MutableColumnPtr create_column() const override;
164143

165144
UInt32 get_scale() const override { return _scale; }

be/src/core/data_type/data_type_date_time.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "core/string_buffer.hpp"
3333
#include "core/types.h"
3434
#include "core/value/vdatetime_value.h"
35+
#include "exprs/function/cast/cast_to_date_or_datetime_impl.hpp"
3536
#include "exprs/function/cast/cast_to_string.h"
3637
#include "util/io_helper.h"
3738

@@ -49,4 +50,19 @@ MutableColumnPtr DataTypeDateTime::create_column() const {
4950
return DataTypeNumberBase<PrimitiveType::TYPE_DATETIME>::create_column();
5051
}
5152

53+
Field DataTypeDateTime::get_field(const TExprNode& node) const {
54+
VecDateTimeValue value;
55+
CastParameters params;
56+
if (CastToDateOrDatetime::from_string_strict_mode<DatelikeParseMode::STRICT,
57+
DatelikeTargetType::DATE_TIME>(
58+
{node.date_literal.value.c_str(), node.date_literal.value.size()}, value, nullptr,
59+
params)) {
60+
value.to_datetime();
61+
return Field::create_field<TYPE_DATETIME>(std::move(value));
62+
} else {
63+
throw doris::Exception(doris::ErrorCode::INVALID_ARGUMENT,
64+
"Invalid value: {} for type DateTime", node.date_literal.value);
65+
}
66+
}
67+
5268
} // namespace doris

be/src/core/data_type/data_type_date_time.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,7 @@ class DataTypeDateTime final : public DataTypeNumberBase<PrimitiveType::TYPE_DAT
9191
return std::make_shared<SerDeType>(nesting_level);
9292
}
9393

94-
Field get_field(const TExprNode& node) const override {
95-
VecDateTimeValue value;
96-
if (value.from_date_str(node.date_literal.value.c_str(), node.date_literal.value.size())) {
97-
value.to_datetime();
98-
return Field::create_field<TYPE_DATETIME>(std::move(value));
99-
} else {
100-
throw doris::Exception(doris::ErrorCode::INVALID_ARGUMENT,
101-
"Invalid value: {} for type DateTime", node.date_literal.value);
102-
}
103-
}
94+
Field get_field(const TExprNode& node) const override;
10495

10596
static void cast_to_date_time(VecDateTimeValue& x);
10697

be/src/core/data_type/data_type_timestamptz.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ namespace doris {
2626
Field DataTypeTimeStampTz::get_field(const TExprNode& node) const {
2727
TimestampTzValue res;
2828
CastParameters params {.status = Status::OK(), .is_strict = true};
29-
30-
if (!CastToTimstampTz::from_string(
29+
// FE pass the value with timezone info(using legacy planner), so no `local_time_zone` here is ok.
30+
if (!CastToTimestampTz::from_string(
3131
{node.date_literal.value.c_str(), node.date_literal.value.size()}, res, params,
3232
nullptr, _scale)) [[unlikely]] {
3333
throw doris::Exception(doris::ErrorCode::INVALID_ARGUMENT,

be/src/core/data_type_serde/data_type_date_or_datetime_serde.cpp

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,9 @@ Status DataTypeDateSerDe<T>::_read_column_from_arrow(IColumn& column,
228228
for (auto value_i = start; value_i < end; ++value_i) {
229229
auto val_str = concrete_array->GetString(value_i);
230230
VecDateTimeValue v;
231-
v.from_date_str(val_str.c_str(), val_str.length(), ctz);
231+
CastParameters params;
232+
CastToDateOrDatetime::from_string_non_strict_mode<DatelikeTargetType::DATE_TIME>(
233+
{val_str.c_str(), val_str.length()}, v, &ctz, params);
232234
if constexpr (is_date) {
233235
v.cast_to_date();
234236
}
@@ -331,8 +333,10 @@ Status DataTypeDateSerDe<T>::from_string_batch(
331333
// then we rely on return value to check success.
332334
// return value only represent OK or InvalidArgument for other error(like InternalError) in parser, MUST throw
333335
// Exception!
334-
if (!CastToDateOrDatetime::from_string_non_strict_mode<IsDatetime>(
335-
str, res, options.timezone, params)) [[unlikely]] {
336+
if (!CastToDateOrDatetime::from_string_non_strict_mode < IsDatetime
337+
? DatelikeTargetType::DATE_TIME
338+
: DatelikeTargetType::DATE > (str, res, options.timezone, params))
339+
[[unlikely]] {
336340
col_nullmap.get_data()[i] = true;
337341
//TODO: we should set `for` functions who need it then skip to set default value for null rows.
338342
col_data.get_data()[i] = VecDateTimeValue::FIRST_DAY;
@@ -360,8 +364,10 @@ Status DataTypeDateSerDe<T>::from_string_strict_mode_batch(
360364
}
361365
auto str = col_str.get_data_at(i);
362366
CppType res;
363-
CastToDateOrDatetime::from_string_strict_mode<true, IsDatetime>(str, res, options.timezone,
364-
params);
367+
CastToDateOrDatetime::from_string_strict_mode<DatelikeParseMode::STRICT,
368+
IsDatetime ? DatelikeTargetType::DATE_TIME
369+
: DatelikeTargetType::DATE>(
370+
str, res, options.timezone, params);
365371
// only after we called something with `IS_STRICT = true`, params.status will be set
366372
if (!params.status.ok()) [[unlikely]] {
367373
params.status.prepend(
@@ -386,8 +392,9 @@ Status DataTypeDateSerDe<T>::from_string(StringRef& str, IColumn& column,
386392
// then we rely on return value to check success.
387393
// return value only represent OK or InvalidArgument for other error(like InternalError) in parser, MUST throw
388394
// Exception!
389-
if (!CastToDateOrDatetime::from_string_non_strict_mode<IsDatetime>(str, res, options.timezone,
390-
params)) [[unlikely]] {
395+
if (!CastToDateOrDatetime::from_string_non_strict_mode < IsDatetime
396+
? DatelikeTargetType::DATE_TIME
397+
: DatelikeTargetType::DATE > (str, res, options.timezone, params)) [[unlikely]] {
391398
return Status::InvalidArgument("parse date or datetime fail, string: '{}'",
392399
str.to_string());
393400
}
@@ -417,8 +424,10 @@ Status DataTypeDateSerDe<T>::from_olap_string(const std::string& str, Field& fie
417424
// then we rely on return value to check success.
418425
// return value only represent OK or InvalidArgument for other error(like InternalError) in parser, MUST throw
419426
// Exception!
420-
if (!CastToDateOrDatetime::from_string_non_strict_mode<IsDatetime>(
421-
StringRef(str), res, options.timezone, params)) [[unlikely]] {
427+
if (!CastToDateOrDatetime::from_string_non_strict_mode < IsDatetime
428+
? DatelikeTargetType::DATE_TIME
429+
: DatelikeTargetType::DATE > (StringRef(str), res, options.timezone, params))
430+
[[unlikely]] {
422431
return Status::InvalidArgument("parse date or datetime fail, string: '{}'", str);
423432
}
424433
field = Field::create_field<T>(std::move(res));
@@ -433,8 +442,10 @@ Status DataTypeDateSerDe<T>::from_string_strict_mode(StringRef& str, IColumn& co
433442
CastParameters params {.status = Status::OK(), .is_strict = true};
434443

435444
CppType res;
436-
CastToDateOrDatetime::from_string_strict_mode<true, IsDatetime>(str, res, options.timezone,
437-
params);
445+
CastToDateOrDatetime::from_string_strict_mode<DatelikeParseMode::STRICT,
446+
IsDatetime ? DatelikeTargetType::DATE_TIME
447+
: DatelikeTargetType::DATE>(
448+
str, res, options.timezone, params);
438449
// only after we called something with `IS_STRICT = true`, params.status will be set
439450
if (!params.status.ok()) [[unlikely]] {
440451
params.status.prepend(fmt::format("parse {} to {} failed: ", str.to_string_view(), name()));
@@ -457,8 +468,10 @@ Status DataTypeDateSerDe<T>::from_int_batch(const typename IntDataType::ColumnTy
457468
CastParameters params {.status = Status::OK(), .is_strict = false};
458469
for (size_t i = 0; i < int_col.size(); ++i) {
459470
CppType val;
460-
if (CastToDateOrDatetime::from_integer<false, IsDatetime>(int_col.get_element(i), val,
461-
params)) [[likely]] {
471+
if (CastToDateOrDatetime::from_integer < DatelikeParseMode::NON_STRICT,
472+
IsDatetime ? DatelikeTargetType::DATE_TIME
473+
: DatelikeTargetType::DATE > (int_col.get_element(i), val, params))
474+
[[likely]] {
462475
// did cast_to_type in `from_integer`
463476
col_data.get_data()[i] = val;
464477
col_nullmap.get_data()[i] = false;
@@ -480,7 +493,10 @@ Status DataTypeDateSerDe<T>::from_int_strict_mode_batch(
480493
CastParameters params {.status = Status::OK(), .is_strict = true};
481494
for (size_t i = 0; i < int_col.size(); ++i) {
482495
CppType val;
483-
CastToDateOrDatetime::from_integer<true, IsDatetime>(int_col.get_element(i), val, params);
496+
CastToDateOrDatetime::from_integer<DatelikeParseMode::STRICT,
497+
IsDatetime ? DatelikeTargetType::DATE_TIME
498+
: DatelikeTargetType::DATE>(
499+
int_col.get_element(i), val, params);
484500
if (!params.status.ok()) [[unlikely]] {
485501
params.status.prepend(
486502
fmt::format("parse {} to {} failed: ", int_col.get_element(i), name()));
@@ -504,8 +520,10 @@ Status DataTypeDateSerDe<T>::from_float_batch(const typename FloatDataType::Colu
504520
CastParameters params {.status = Status::OK(), .is_strict = false};
505521
for (size_t i = 0; i < float_col.size(); ++i) {
506522
CppType val;
507-
if (CastToDateOrDatetime::from_float<false, IsDatetime>(float_col.get_data()[i], val,
508-
params)) [[likely]] {
523+
if (CastToDateOrDatetime::from_float < DatelikeParseMode::NON_STRICT,
524+
IsDatetime ? DatelikeTargetType::DATE_TIME
525+
: DatelikeTargetType::DATE > (float_col.get_data()[i], val, 0, params))
526+
[[likely]] {
509527
col_data.get_data()[i] = val;
510528
col_nullmap.get_data()[i] = false;
511529
} else {
@@ -526,7 +544,10 @@ Status DataTypeDateSerDe<T>::from_float_strict_mode_batch(
526544
CastParameters params {.status = Status::OK(), .is_strict = true};
527545
for (size_t i = 0; i < float_col.size(); ++i) {
528546
CppType val;
529-
CastToDateOrDatetime::from_float<true, IsDatetime>(float_col.get_data()[i], val, params);
547+
CastToDateOrDatetime::from_float<DatelikeParseMode::STRICT,
548+
IsDatetime ? DatelikeTargetType::DATE_TIME
549+
: DatelikeTargetType::DATE>(
550+
float_col.get_data()[i], val, 0, params);
530551
if (!params.status.ok()) [[unlikely]] {
531552
params.status.prepend(
532553
fmt::format("parse {} to {} failed: ", float_col.get_data()[i], name()));
@@ -550,9 +571,12 @@ Status DataTypeDateSerDe<T>::from_decimal_batch(
550571
CastParameters params {.status = Status::OK(), .is_strict = false};
551572
for (size_t i = 0; i < decimal_col.size(); ++i) {
552573
CppType val;
553-
if (CastToDateOrDatetime::from_decimal<true, IsDatetime>(
554-
decimal_col.get_intergral_part(i), decimal_col.get_fractional_part(i),
555-
decimal_col.get_scale(), val, params)) [[likely]] {
574+
if (CastToDateOrDatetime::from_decimal < DatelikeParseMode::NON_STRICT,
575+
IsDatetime ? DatelikeTargetType::DATE_TIME
576+
: DatelikeTargetType::DATE > (decimal_col.get_intergral_part(i),
577+
decimal_col.get_fractional_part(i),
578+
decimal_col.get_scale(), val, params))
579+
[[likely]] {
556580
col_data.get_data()[i] = val;
557581
col_nullmap.get_data()[i] = false;
558582
} else {
@@ -573,9 +597,11 @@ Status DataTypeDateSerDe<T>::from_decimal_strict_mode_batch(
573597
CastParameters params {.status = Status::OK(), .is_strict = true};
574598
for (size_t i = 0; i < decimal_col.size(); ++i) {
575599
CppType val;
576-
CastToDateOrDatetime::from_decimal<true, IsDatetime>(decimal_col.get_intergral_part(i),
577-
decimal_col.get_fractional_part(i),
578-
decimal_col.get_scale(), val, params);
600+
CastToDateOrDatetime::from_decimal<DatelikeParseMode::STRICT,
601+
IsDatetime ? DatelikeTargetType::DATE_TIME
602+
: DatelikeTargetType::DATE>(
603+
decimal_col.get_intergral_part(i), decimal_col.get_fractional_part(i),
604+
decimal_col.get_scale(), val, params);
579605
if (!params.status.ok()) [[unlikely]] {
580606
params.status.prepend(
581607
fmt::format("parse {}.{} to {} failed: ", decimal_col.get_intergral_part(i),

0 commit comments

Comments
 (0)