This is an automated email from the ASF dual-hosted git repository.
gabriellee pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 8973610543 [feature](datetime) "timediff" supports calculating
microseconds (#21371)
8973610543 is described below
commit 89736105434c23787afd412ce808a17ca55d6cf0
Author: Mryange <[email protected]>
AuthorDate: Mon Jul 10 19:21:32 2023 +0800
[feature](datetime) "timediff" supports calculating microseconds (#21371)
---
be/src/runtime/types.cpp | 2 +-
be/src/util/date_func.cpp | 94 +++++++++++++++++++++
be/src/util/date_func.h | 3 +-
be/src/util/mysql_row_buffer.cpp | 26 ++++++
be/src/util/mysql_row_buffer.h | 1 +
be/src/vec/core/call_on_type_index.h | 2 +
be/src/vec/data_types/data_type.cpp | 2 +
be/src/vec/data_types/data_type_factory.cpp | 10 ++-
be/src/vec/data_types/data_type_time.cpp | 28 +++++++
be/src/vec/data_types/data_type_time.h | 36 ++++++++
.../vec/data_types/serde/data_type_time_serde.cpp | 21 +++++
be/src/vec/data_types/serde/data_type_time_serde.h | 14 ++++
be/src/vec/exec/format/parquet/schema_desc.cpp | 2 +-
.../function_date_or_datetime_computation.h | 43 +++++++++-
be/src/vec/functions/function_running_difference.h | 4 +-
be/src/vec/runtime/vdatetime_value.h | 20 +++++
be/src/vec/sink/vmysql_result_writer.cpp | 5 +-
be/test/vec/function/function_time_test.cpp | 7 +-
.../java/org/apache/doris/catalog/ScalarType.java | 13 ++-
.../main/java/org/apache/doris/analysis/Expr.java | 4 +-
.../apache/doris/analysis/FunctionCallExpr.java | 7 ++
.../expressions/functions/scalar/TimeDiff.java | 42 +++++++++-
.../org/apache/doris/nereids/types/TimeV2Type.java | 20 ++++-
gensrc/proto/types.proto | 1 +
.../correctness/test_time_diff_microseconds.out | 29 +++++++
.../data/nereids_p0/test_char_implicit_cast.out | 6 +-
.../correctness/test_time_diff_microseconds.groovy | 95 ++++++++++++++++++++++
.../nereids_p0/test_char_implicit_cast.groovy | 4 +-
28 files changed, 519 insertions(+), 22 deletions(-)
diff --git a/be/src/runtime/types.cpp b/be/src/runtime/types.cpp
index ebcb6eb378..8cbeebe4c3 100644
--- a/be/src/runtime/types.cpp
+++ b/be/src/runtime/types.cpp
@@ -46,7 +46,7 @@ TypeDescriptor::TypeDescriptor(const std::vector<TTypeNode>&
types, int* idx)
DCHECK(scalar_type.__isset.len);
len = scalar_type.len;
} else if (type == TYPE_DECIMALV2 || type == TYPE_DECIMAL32 || type ==
TYPE_DECIMAL64 ||
- type == TYPE_DECIMAL128I || type == TYPE_DATETIMEV2) {
+ type == TYPE_DECIMAL128I || type == TYPE_DATETIMEV2 || type
== TYPE_TIMEV2) {
DCHECK(scalar_type.__isset.precision);
DCHECK(scalar_type.__isset.scale);
precision = scalar_type.precision;
diff --git a/be/src/util/date_func.cpp b/be/src/util/date_func.cpp
index e7ae30e003..6ca199d630 100644
--- a/be/src/util/date_func.cpp
+++ b/be/src/util/date_func.cpp
@@ -86,6 +86,7 @@ uint64_t timestamp_from_datetime_v2(const std::string&
date_str) {
}
// refer to https://dev.mysql.com/doc/refman/5.7/en/time.html
// the time value between '-838:59:59' and '838:59:59'
+/// TODO: Why is the time type stored as double? Can we directly use int64 and
remove the time limit?
int32_t time_to_buffer_from_double(double time, char* buffer) {
char* begin = buffer;
if (time < 0) {
@@ -115,6 +116,52 @@ int32_t time_to_buffer_from_double(double time, char*
buffer) {
return buffer - begin;
}
+int32_t timev2_to_buffer_from_double(double time, char* buffer, int scale) {
+ static int pow10[7] = {1, 10, 100, 1000, 10000, 100000, 1000000};
+ char* begin = buffer;
+ if (time < 0) {
+ time = -time;
+ *buffer++ = '-';
+ }
+ int64_t m_time = time;
+ // m_time = hour * 3600 * 1000 * 1000 + minute * 60 * 1000 * 1000 + second
* 1000 * 1000 + microsecond
+ int64_t hour = m_time / ((int64_t)3600 * 1000 * 1000);
+ if (hour >= 100) {
+ auto f = fmt::format_int(hour);
+ memcpy(buffer, f.data(), f.size());
+ buffer = buffer + f.size();
+ } else {
+ *buffer++ = (char)('0' + (hour / 10));
+ *buffer++ = (char)('0' + (hour % 10));
+ }
+ *buffer++ = ':';
+ m_time %= (int64_t)3600 * 1000 * 1000;
+ int64_t minute = m_time / (60 * 1000 * 1000);
+ *buffer++ = (char)('0' + (minute / 10));
+ *buffer++ = (char)('0' + (minute % 10));
+ *buffer++ = ':';
+ m_time %= 60 * 1000 * 1000;
+ int32_t second = m_time / (1000 * 1000);
+ *buffer++ = (char)('0' + (second / 10));
+ *buffer++ = (char)('0' + (second % 10));
+ m_time %= 1000 * 1000;
+ if (scale == 0) {
+ return buffer - begin;
+ }
+ *buffer++ = '.';
+ memset(buffer, '0', scale);
+ buffer += scale;
+ int32_t micosecond = m_time % (1000 * 1000);
+ micosecond /= pow10[6 - scale];
+ auto it = buffer - 1;
+ while (micosecond) {
+ *it = (char)('0' + (micosecond % 10));
+ micosecond /= 10;
+ it--;
+ }
+ return buffer - begin;
+}
+
std::string time_to_buffer_from_double(double time) {
fmt::memory_buffer buffer;
if (time < 0) {
@@ -136,4 +183,51 @@ std::string time_to_buffer_from_double(double time) {
return fmt::to_string(buffer);
}
+std::string timev2_to_buffer_from_double(double time, int scale) {
+ static int pow10[7] = {1, 10, 100, 1000, 10000, 100000, 1000000};
+ fmt::memory_buffer buffer;
+ if (time < 0) {
+ time = -time;
+ fmt::format_to(buffer, "-");
+ }
+ int64_t m_time = time;
+ // m_time = hour * 3600 * 1000 * 1000 + minute * 60 * 1000 * 1000 + second
* 1000 * 1000 + microsecond
+ int64_t hour = m_time / ((int64_t)3600 * 1000 * 1000);
+ if (hour >= 100) {
+ fmt::format_to(buffer, fmt::format("{}", hour));
+ } else {
+ fmt::format_to(buffer, fmt::format("{:02d}", hour));
+ }
+ m_time %= (int64_t)3600 * 1000 * 1000;
+ int64_t minute = m_time / (60 * 1000 * 1000);
+ m_time %= 60 * 1000 * 1000;
+ int32_t second = m_time / (1000 * 1000);
+ int32_t micosecond = m_time % (1000 * 1000);
+ micosecond /= pow10[6 - scale];
+ switch (scale) {
+ case 0:
+ fmt::format_to(buffer, fmt::format(":{:02d}:{:02d}", minute, second,
micosecond));
+ break;
+ case 1:
+ fmt::format_to(buffer, fmt::format(":{:02d}:{:02d}.{:01d}", minute,
second, micosecond));
+ break;
+ case 2:
+ fmt::format_to(buffer, fmt::format(":{:02d}:{:02d}.{:02d}", minute,
second, micosecond));
+ break;
+ case 3:
+ fmt::format_to(buffer, fmt::format(":{:02d}:{:02d}.{:03d}", minute,
second, micosecond));
+ break;
+ case 4:
+ fmt::format_to(buffer, fmt::format(":{:02d}:{:02d}.{:04d}", minute,
second, micosecond));
+ break;
+ case 5:
+ fmt::format_to(buffer, fmt::format(":{:02d}:{:02d}.{:05d}", minute,
second, micosecond));
+ break;
+ case 6:
+ fmt::format_to(buffer, fmt::format(":{:02d}:{:02d}.{:06d}", minute,
second, micosecond));
+ break;
+ }
+
+ return fmt::to_string(buffer);
+}
} // namespace doris
diff --git a/be/src/util/date_func.h b/be/src/util/date_func.h
index cc9aa7b6da..405132c3f0 100644
--- a/be/src/util/date_func.h
+++ b/be/src/util/date_func.h
@@ -26,9 +26,10 @@ namespace doris {
uint64_t timestamp_from_datetime(const std::string& datetime_str);
uint32_t timestamp_from_date(const std::string& date_str);
int32_t time_to_buffer_from_double(double time, char* buffer);
+int32_t timev2_to_buffer_from_double(double time, char* buffer, int scale);
uint32_t timestamp_from_date_v2(const std::string& date_str);
uint64_t timestamp_from_datetime_v2(const std::string& date_str);
std::string time_to_buffer_from_double(double time);
-
+std::string timev2_to_buffer_from_double(double time, int scale);
} // namespace doris
diff --git a/be/src/util/mysql_row_buffer.cpp b/be/src/util/mysql_row_buffer.cpp
index a8d2674073..00193481b7 100644
--- a/be/src/util/mysql_row_buffer.cpp
+++ b/be/src/util/mysql_row_buffer.cpp
@@ -195,6 +195,13 @@ static char* add_time(double data, char* pos, bool
dynamic_mode) {
return pos + length;
}
+static char* add_timev2(double data, char* pos, bool dynamic_mode, int scale) {
+ int length = timev2_to_buffer_from_double(data, pos + !dynamic_mode,
scale);
+ if (!dynamic_mode) {
+ int1store(pos++, length);
+ }
+ return pos + length;
+}
template <typename DateType>
static char* add_datetime(const DateType& data, char* pos, bool dynamic_mode) {
int length = data.to_buffer(pos + !dynamic_mode);
@@ -422,6 +429,25 @@ int MysqlRowBuffer<is_binary_format>::push_time(double
data) {
return 0;
}
+template <bool is_binary_format>
+int MysqlRowBuffer<is_binary_format>::push_timev2(double data, int scale) {
+ if (is_binary_format && !_dynamic_mode) {
+ char buff[8];
+ _field_pos++;
+ float8store(buff, data);
+ return append(buff, 8);
+ }
+ // 1 for string trail, 1 for length, other for time str
+ int ret = reserve(2 + MAX_TIME_WIDTH);
+
+ if (0 != ret) {
+ LOG(ERROR) << "mysql row buffer reserve failed.";
+ return ret;
+ }
+
+ _pos = add_timev2(data, _pos, _dynamic_mode, scale);
+ return 0;
+}
template <bool is_binary_format>
template <typename DateType>
int MysqlRowBuffer<is_binary_format>::push_vec_datetime(DateType& data) {
diff --git a/be/src/util/mysql_row_buffer.h b/be/src/util/mysql_row_buffer.h
index 2df739450f..6a6c2b0ad0 100644
--- a/be/src/util/mysql_row_buffer.h
+++ b/be/src/util/mysql_row_buffer.h
@@ -72,6 +72,7 @@ public:
int push_float(float data);
int push_double(double data);
int push_time(double data);
+ int push_timev2(double data, int scale);
template <typename DateType>
int push_datetime(const DateType& data);
int push_decimal(const DecimalV2Value& data, int round_scale);
diff --git a/be/src/vec/core/call_on_type_index.h
b/be/src/vec/core/call_on_type_index.h
index bc822d9902..7e6ea4118c 100644
--- a/be/src/vec/core/call_on_type_index.h
+++ b/be/src/vec/core/call_on_type_index.h
@@ -205,6 +205,8 @@ bool call_on_index_and_data_type(TypeIndex number, F&& f) {
return f(TypePair<DataTypeNumber<Float64>, T>());
case TypeIndex::Time:
return f(TypePair<DataTypeTime, T>());
+ case TypeIndex::TimeV2:
+ return f(TypePair<DataTypeTimeV2, T>());
case TypeIndex::Decimal32:
return f(TypePair<DataTypeDecimal<Decimal32>, T>());
case TypeIndex::Decimal64:
diff --git a/be/src/vec/data_types/data_type.cpp
b/be/src/vec/data_types/data_type.cpp
index cf66b64f95..7ab7a99ca3 100644
--- a/be/src/vec/data_types/data_type.cpp
+++ b/be/src/vec/data_types/data_type.cpp
@@ -171,6 +171,8 @@ PGenericType_TypeId IDataType::get_pdata_type(const
IDataType* data_type) {
return PGenericType::TIME;
case TypeIndex::AggState:
return PGenericType::AGG_STATE;
+ case TypeIndex::TimeV2:
+ return PGenericType::TIMEV2;
default:
return PGenericType::UNKNOWN;
}
diff --git a/be/src/vec/data_types/data_type_factory.cpp
b/be/src/vec/data_types/data_type_factory.cpp
index 366cc04c89..48dbbd3270 100644
--- a/be/src/vec/data_types/data_type_factory.cpp
+++ b/be/src/vec/data_types/data_type_factory.cpp
@@ -144,9 +144,11 @@ DataTypePtr DataTypeFactory::create_data_type(const
TypeDescriptor& col_desc, bo
nested = std::make_shared<vectorized::DataTypeDateTime>();
break;
case TYPE_TIME:
- case TYPE_TIMEV2:
nested = std::make_shared<vectorized::DataTypeTime>();
break;
+ case TYPE_TIMEV2:
+ nested = std::make_shared<vectorized::DataTypeTimeV2>(col_desc.scale);
+ break;
case TYPE_DOUBLE:
nested = std::make_shared<vectorized::DataTypeFloat64>();
break;
@@ -314,7 +316,7 @@ DataTypePtr DataTypeFactory::create_data_type(const
TypeIndex& type_index, bool
nested = std::make_shared<vectorized::DataTypeQuantileStateDouble>();
break;
case TypeIndex::TimeV2:
- nested = std::make_shared<vectorized::DataTypeTime>();
+ nested = std::make_shared<vectorized::DataTypeTimeV2>();
break;
default:
DCHECK(false) << "invalid typeindex:" << getTypeName(type_index);
@@ -518,6 +520,10 @@ DataTypePtr DataTypeFactory::create_data_type(const
PColumnMeta& pcolumn) {
nested = std::make_shared<DataTypeTime>();
break;
}
+ case PGenericType::TIMEV2: {
+ nested =
std::make_shared<DataTypeTimeV2>(pcolumn.decimal_param().scale());
+ break;
+ }
case PGenericType::AGG_STATE: {
DataTypes sub_types;
for (auto child : pcolumn.children()) {
diff --git a/be/src/vec/data_types/data_type_time.cpp
b/be/src/vec/data_types/data_type_time.cpp
index 7881124f3e..d9c2af440e 100644
--- a/be/src/vec/data_types/data_type_time.cpp
+++ b/be/src/vec/data_types/data_type_time.cpp
@@ -20,6 +20,8 @@
#include "vec/data_types/data_type_time.h"
+#include <gen_cpp/data.pb.h>
+
#include <typeinfo>
#include <utility>
@@ -56,8 +58,34 @@ void DataTypeTime::to_string(const IColumn& column, size_t
row_num, BufferWritab
ostr.write(value.data(), value.size());
}
+void DataTypeTimeV2::to_pb_column_meta(PColumnMeta* col_meta) const {
+ IDataType::to_pb_column_meta(col_meta);
+ col_meta->mutable_decimal_param()->set_scale(_scale);
+}
+
MutableColumnPtr DataTypeTime::create_column() const {
return DataTypeNumberBase<Float64>::create_column();
}
+bool DataTypeTimeV2::equals(const IDataType& rhs) const {
+ return typeid(rhs) == typeid(*this);
+}
+
+std::string DataTypeTimeV2::to_string(const IColumn& column, size_t row_num)
const {
+ auto result = check_column_const_set_readability(column, row_num);
+ ColumnPtr ptr = result.first;
+ row_num = result.second;
+
+ auto value = assert_cast<const ColumnFloat64&>(*ptr).get_element(row_num);
+ return timev2_to_buffer_from_double(value, _scale);
+}
+
+void DataTypeTimeV2::to_string(const IColumn& column, size_t row_num,
BufferWritable& ostr) const {
+ std::string value = to_string(column, row_num);
+ ostr.write(value.data(), value.size());
+}
+
+MutableColumnPtr DataTypeTimeV2::create_column() const {
+ return DataTypeNumberBase<Float64>::create_column();
+}
} // namespace doris::vectorized
diff --git a/be/src/vec/data_types/data_type_time.h
b/be/src/vec/data_types/data_type_time.h
index 0bc1b1aad8..47311804e9 100644
--- a/be/src/vec/data_types/data_type_time.h
+++ b/be/src/vec/data_types/data_type_time.h
@@ -69,6 +69,42 @@ public:
DataTypeSerDeSPtr get_serde() const override { return
std::make_shared<DataTypeTimeSerDe>(); };
TypeIndex get_type_id() const override { return TypeIndex::Time; }
+ const char* get_family_name() const override { return "time"; }
};
+class DataTypeTimeV2 final : public DataTypeNumberBase<Float64> {
+public:
+ DataTypeTimeV2(int scale = 0) : _scale(scale) {
+ if (UNLIKELY(scale > 6)) {
+ LOG(FATAL) << fmt::format("Scale {} is out of bounds", scale);
+ }
+ }
+ bool equals(const IDataType& rhs) const override;
+
+ std::string to_string(const IColumn& column, size_t row_num) const
override;
+ PrimitiveType get_type_as_primitive_type() const override { return
TYPE_TIMEV2; }
+ TPrimitiveType::type get_type_as_tprimitive_type() const override {
+ return TPrimitiveType::TIMEV2;
+ }
+
+ void to_string(const IColumn& column, size_t row_num, BufferWritable&
ostr) const override;
+
+ MutableColumnPtr create_column() const override;
+
+ bool is_summable() const override { return true; }
+ bool can_be_used_in_bit_operations() const override { return true; }
+ bool can_be_used_in_boolean_context() const override { return true; }
+ bool can_be_inside_nullable() const override { return true; }
+
+ void to_pb_column_meta(PColumnMeta* col_meta) const override;
+ DataTypeSerDeSPtr get_serde() const override {
+ return std::make_shared<DataTypeTimeV2SerDe>(_scale);
+ };
+ TypeIndex get_type_id() const override { return TypeIndex::TimeV2; }
+ const char* get_family_name() const override { return "timev2"; }
+ UInt32 get_scale() const override { return _scale; }
+
+private:
+ UInt32 _scale;
+};
} // namespace doris::vectorized
diff --git a/be/src/vec/data_types/serde/data_type_time_serde.cpp
b/be/src/vec/data_types/serde/data_type_time_serde.cpp
index 46d5989a0b..4fed145c8b 100644
--- a/be/src/vec/data_types/serde/data_type_time_serde.cpp
+++ b/be/src/vec/data_types/serde/data_type_time_serde.cpp
@@ -43,5 +43,26 @@ Status DataTypeTimeSerDe::write_column_to_mysql(const
IColumn& column,
return _write_column_to_mysql(column, row_buffer, row_idx, col_const);
}
+Status DataTypeTimeV2SerDe::write_column_to_mysql(const IColumn& column,
+ MysqlRowBuffer<true>&
row_buffer, int row_idx,
+ bool col_const) const {
+ return _write_column_to_mysql(column, row_buffer, row_idx, col_const);
+}
+Status DataTypeTimeV2SerDe::write_column_to_mysql(const IColumn& column,
+ MysqlRowBuffer<false>&
row_buffer, int row_idx,
+ bool col_const) const {
+ return _write_column_to_mysql(column, row_buffer, row_idx, col_const);
+}
+template <bool is_binary_format>
+Status DataTypeTimeV2SerDe::_write_column_to_mysql(const IColumn& column,
+
MysqlRowBuffer<is_binary_format>& result,
+ int row_idx, bool
col_const) const {
+ auto& data = assert_cast<const ColumnVector<Float64>&>(column).get_data();
+ const auto col_index = index_check_const(row_idx, col_const);
+ if (UNLIKELY(0 != result.push_timev2(data[col_index], scale))) {
+ return Status::InternalError("pack mysql buffer failed.");
+ }
+ return Status::OK();
+}
} // namespace vectorized
} // namespace doris
\ No newline at end of file
diff --git a/be/src/vec/data_types/serde/data_type_time_serde.h
b/be/src/vec/data_types/serde/data_type_time_serde.h
index 3c1c14f977..c96163be55 100644
--- a/be/src/vec/data_types/serde/data_type_time_serde.h
+++ b/be/src/vec/data_types/serde/data_type_time_serde.h
@@ -41,5 +41,19 @@ private:
Status _write_column_to_mysql(const IColumn& column,
MysqlRowBuffer<is_binary_format>& result,
int row_idx, bool col_const) const;
};
+class DataTypeTimeV2SerDe : public DataTypeNumberSerDe<Float64> {
+public:
+ DataTypeTimeV2SerDe(int scale = -1) : scale(scale) {};
+ Status write_column_to_mysql(const IColumn& column, MysqlRowBuffer<true>&
row_buffer,
+ int row_idx, bool col_const) const override;
+ Status write_column_to_mysql(const IColumn& column, MysqlRowBuffer<false>&
row_buffer,
+ int row_idx, bool col_const) const override;
+
+private:
+ template <bool is_binary_format>
+ Status _write_column_to_mysql(const IColumn& column,
MysqlRowBuffer<is_binary_format>& result,
+ int row_idx, bool col_const) const;
+ int scale;
+};
} // namespace vectorized
} // namespace doris
\ No newline at end of file
diff --git a/be/src/vec/exec/format/parquet/schema_desc.cpp
b/be/src/vec/exec/format/parquet/schema_desc.cpp
index 6d87425983..f52e3921ad 100644
--- a/be/src/vec/exec/format/parquet/schema_desc.cpp
+++ b/be/src/vec/exec/format/parquet/schema_desc.cpp
@@ -237,7 +237,7 @@ TypeDescriptor
FieldDescriptor::convert_to_doris_type(tparquet::LogicalType logi
}
}
} else if (logicalType.__isset.TIME) {
- type = TypeDescriptor(TYPE_TIMEV2);
+ type = TypeDescriptor(TYPE_TIME);
} else if (logicalType.__isset.TIMESTAMP) {
type = TypeDescriptor(TYPE_DATETIMEV2);
const auto& time_unit = logicalType.TIMESTAMP.unit;
diff --git a/be/src/vec/functions/function_date_or_datetime_computation.h
b/be/src/vec/functions/function_date_or_datetime_computation.h
index e31d8f64c6..1bc5d37c53 100644
--- a/be/src/vec/functions/function_date_or_datetime_computation.h
+++ b/be/src/vec/functions/function_date_or_datetime_computation.h
@@ -281,8 +281,47 @@ struct SubtractYearsImpl :
SubtractIntervalImpl<AddYearsImpl<DateType>, DateType
}
\
};
DECLARE_DATE_FUNCTIONS(DateDiffImpl, datediff, DataTypeInt32, (ts0.daynr() -
ts1.daynr()));
-DECLARE_DATE_FUNCTIONS(TimeDiffImpl, timediff, DataTypeTime,
ts0.second_diff(ts1));
-
+// DECLARE_DATE_FUNCTIONS(TimeDiffImpl, timediff, DataTypeTime,
ts0.second_diff(ts1));
+// Expands to
+template <typename DateType1, typename DateType2>
+struct TimeDiffImpl {
+ using ArgType1 = std ::conditional_t<
+ std ::is_same_v<DateType1, DataTypeDateV2>, UInt32,
+ std ::conditional_t<std ::is_same_v<DateType1,
DataTypeDateTimeV2>, UInt64, Int64>>;
+ using ArgType2 = std ::conditional_t<
+ std ::is_same_v<DateType2, DataTypeDateV2>, UInt32,
+ std ::conditional_t<std ::is_same_v<DateType2,
DataTypeDateTimeV2>, UInt64, Int64>>;
+ using DateValueType1 = std ::conditional_t<
+ std ::is_same_v<DateType1, DataTypeDateV2>,
DateV2Value<DateV2ValueType>,
+ std ::conditional_t<std ::is_same_v<DateType1, DataTypeDateTimeV2>,
+ DateV2Value<DateTimeV2ValueType>,
VecDateTimeValue>>;
+ using DateValueType2 = std ::conditional_t<
+ std ::is_same_v<DateType2, DataTypeDateV2>,
DateV2Value<DateV2ValueType>,
+ std ::conditional_t<std ::is_same_v<DateType2, DataTypeDateTimeV2>,
+ DateV2Value<DateTimeV2ValueType>,
VecDateTimeValue>>;
+ static constexpr bool UsingTimev2 = std::is_same_v<DateType1,
DataTypeDateTimeV2> ||
+ std::is_same_v<DateType2,
DataTypeDateTimeV2> ||
+ std::is_same_v<DateType1,
DataTypeDateV2> ||
+ std::is_same_v<DateType2,
DataTypeDateV2>;
+ using ReturnType = std ::conditional_t<UsingTimev2, DataTypeTimeV2,
DataTypeTime>;
+
+ static constexpr auto name = "timediff";
+ static constexpr auto is_nullable = false;
+ static inline ReturnType ::FieldType execute(const ArgType1& t0, const
ArgType2& t1,
+ bool& is_null) {
+ const auto& ts0 = reinterpret_cast<const DateValueType1&>(t0);
+ const auto& ts1 = reinterpret_cast<const DateValueType2&>(t1);
+ is_null = !ts0.is_valid_date() || !ts1.is_valid_date();
+ if constexpr (UsingTimev2) {
+ return ts0.microsecond_diff(ts1);
+ } else {
+ return ts0.second_diff(ts1);
+ }
+ }
+ static DataTypes get_variadic_argument_types() {
+ return {std ::make_shared<DateType1>(), std
::make_shared<DateType2>()};
+ }
+};
#define TIME_DIFF_FUNCTION_IMPL(CLASS, NAME, UNIT) \
DECLARE_DATE_FUNCTIONS(CLASS, NAME, DataTypeInt64,
datetime_diff<TimeUnit::UNIT>(ts1, ts0))
diff --git a/be/src/vec/functions/function_running_difference.h
b/be/src/vec/functions/function_running_difference.h
index 248c29a9a5..73446dd2b2 100644
--- a/be/src/vec/functions/function_running_difference.h
+++ b/be/src/vec/functions/function_running_difference.h
@@ -82,8 +82,10 @@ public:
return_type = std::make_shared<DataTypeFloat64>();
} else if (which.is_decimal()) {
return_type = nested_type;
- } else if (which.is_date_time() || which.is_date_time_v2()) {
+ } else if (which.is_date_time()) {
return_type = std::make_shared<DataTypeTime>();
+ } else if (which.is_date_time_v2()) {
+ return_type = std::make_shared<DataTypeTimeV2>();
} else if (which.is_date() || which.is_date_v2()) {
return_type = std::make_shared<DataTypeInt32>();
}
diff --git a/be/src/vec/runtime/vdatetime_value.h
b/be/src/vec/runtime/vdatetime_value.h
index 891d6fb8b1..639d7171cb 100644
--- a/be/src/vec/runtime/vdatetime_value.h
+++ b/be/src/vec/runtime/vdatetime_value.h
@@ -870,6 +870,10 @@ public:
return hour() * SECOND_PER_HOUR + minute() * SECOND_PER_MINUTE +
second();
}
+ int64_t time_part_to_microsecond() const {
+ return time_part_to_seconds() * 1000 * 1000 + microsecond();
+ }
+
uint16_t year() const { return date_v2_value_.year_; }
uint8_t month() const { return date_v2_value_.month_; }
int quarter() const { return (date_v2_value_.month_ - 1) / 3 + 1; }
@@ -1065,11 +1069,27 @@ public:
return time_part_to_seconds() - rhs.time_part_to_seconds();
}
+ //only calculate the diff of dd:mm:ss.SSSSSS
+ template <typename RHS>
+ int64_t time_part_diff_microsecond(const RHS& rhs) const {
+ return time_part_to_microsecond() - rhs.time_part_to_microsecond();
+ }
+
template <typename RHS>
int64_t second_diff(const RHS& rhs) const {
return (daynr() - rhs.daynr()) * SECOND_PER_HOUR * HOUR_PER_DAY +
time_part_diff(rhs);
}
+ template <typename RHS>
+ double microsecond_diff(const RHS& rhs) const {
+ int64_t diff_m = (daynr() - rhs.daynr()) * SECOND_PER_HOUR *
HOUR_PER_DAY * 1000 * 1000 +
+ time_part_diff_microsecond(rhs);
+ if (diff_m > (int64_t)3020399 * 1000 * 1000) {
+ diff_m = (int64_t)3020399 * 1000 * 1000;
+ }
+ return diff_m;
+ }
+
bool can_cast_to_date_without_loss_accuracy() {
return this->hour() == 0 && this->minute() == 0 && this->second() == 0
&&
this->microsecond() == 0;
diff --git a/be/src/vec/sink/vmysql_result_writer.cpp
b/be/src/vec/sink/vmysql_result_writer.cpp
index fd600e0bee..6c42403343 100644
--- a/be/src/vec/sink/vmysql_result_writer.cpp
+++ b/be/src/vec/sink/vmysql_result_writer.cpp
@@ -368,9 +368,12 @@ Status
VMysqlResultWriter<is_binary_format>::_add_one_column(
if constexpr (type == TYPE_DOUBLE) {
buf_ret = rows_buffer[i].push_double(data[col_index]);
}
- if constexpr (type == TYPE_TIME || type == TYPE_TIMEV2) {
+ if constexpr (type == TYPE_TIME) {
buf_ret = rows_buffer[i].push_time(data[col_index]);
}
+ if constexpr (type == TYPE_TIMEV2) {
+ buf_ret = rows_buffer[i].push_timev2(data[col_index]);
+ }
if constexpr (type == TYPE_DATETIME) {
auto time_num = data[col_index];
VecDateTimeValue time_val = binary_cast<Int64,
VecDateTimeValue>(time_num);
diff --git a/be/test/vec/function/function_time_test.cpp
b/be/test/vec/function/function_time_test.cpp
index 45f0c96dbd..b93d66cc4a 100644
--- a/be/test/vec/function/function_time_test.cpp
+++ b/be/test/vec/function/function_time_test.cpp
@@ -859,7 +859,7 @@ TEST(VTimestampFunctionsTest, timediff_v2_test) {
{{std::string("2019-00-18"),
std::string("2019-07-18")}, Null()},
{{std::string("2019-07-18"),
std::string("2019-07-00")}, Null()}};
- check_function<DataTypeTime, true>(func_name, input_types, data_set);
+ check_function<DataTypeTimeV2, true>(func_name, input_types, data_set);
}
{
@@ -867,11 +867,12 @@ TEST(VTimestampFunctionsTest, timediff_v2_test) {
DataSet data_set = {
{{std::string("2019-07-18 00:00:00"), std::string("2019-07-18
00:00:00")}, 0.0},
- {{std::string("2019-07-18 00:00:10"), std::string("2019-07-18
00:00:00")}, 10.0},
+ {{std::string("2019-07-18 00:00:10"), std::string("2019-07-18
00:00:00")},
+ 10000000.0},
{{std::string("2019-00-18 00:00:00"), std::string("2019-07-18
00:00:00")}, Null()},
{{std::string("2019-07-18 00:00:00"), std::string("2019-07-00
00:00:00")}, Null()}};
- check_function<DataTypeTime, true>(func_name, input_types, data_set);
+ check_function<DataTypeTimeV2, true>(func_name, input_types, data_set);
}
}
diff --git
a/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java
b/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java
index dc36a9ab70..41cd59d474 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java
@@ -143,6 +143,8 @@ public class ScalarType extends Type {
return createDecimalType(precision, scale);
case DATETIMEV2:
return createDatetimeV2Type(scale);
+ case TIMEV2:
+ return createTimeV2Type(scale);
default:
return createType(type);
}
@@ -687,6 +689,12 @@ public class ScalarType extends Type {
scalarType.setPrecision(precision);
break;
}
+ case TIMEV2: {
+ Preconditions.checkArgument(precision >= scale);
+ scalarType.setScale(scale);
+ scalarType.setPrecision(precision);
+ break;
+ }
default:
break;
}
@@ -867,7 +875,10 @@ public class ScalarType extends Type {
return false;
}
ScalarType other = (ScalarType) o;
- if ((this.isDatetimeV2() && other.isDatetimeV2()) || (this.isTimeV2()
&& other.isTimeV2())) {
+ if ((this.isDatetimeV2() && other.isDatetimeV2())) {
+ return this.decimalScale() == other.decimalScale();
+ }
+ if (this.isTimeV2() && other.isTimeV2()) {
return this.decimalScale() == other.decimalScale();
}
if (type.isDecimalV3Type() && other.isDecimalV3()) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/Expr.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/Expr.java
index d56c398836..705a45ca37 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/Expr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/Expr.java
@@ -2457,7 +2457,7 @@ public abstract class Expr extends TreeNode<Expr>
implements ParseNode, Cloneabl
}
}
- protected Type getActualScalarType(Type originType) {
+ protected Type getActualScalarType(Type originType) {
if (originType.getPrimitiveType() == PrimitiveType.DECIMAL32) {
return Type.DECIMAL32;
} else if (originType.getPrimitiveType() == PrimitiveType.DECIMAL64) {
@@ -2472,6 +2472,8 @@ public abstract class Expr extends TreeNode<Expr>
implements ParseNode, Cloneabl
return Type.CHAR;
} else if (originType.getPrimitiveType() == PrimitiveType.DECIMALV2) {
return Type.MAX_DECIMALV2_TYPE;
+ } else if (originType.getPrimitiveType() == PrimitiveType.TIMEV2) {
+ return Type.TIMEV2;
}
return originType;
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
index 24169eccf8..4e57ee9df0 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
@@ -1559,6 +1559,13 @@ public class FunctionCallExpr extends Expr {
}
if (fn.getFunctionName().getFunction().equals("timediff")) {
fn.getReturnType().getPrimitiveType().setTimeType();
+ ScalarType left = (ScalarType) argTypes[0];
+ ScalarType right = (ScalarType) argTypes[1];
+ if (left.isDatetimeV2() || right.isDatetimeV2() || left.isDateV2()
|| right.isDateV2()) {
+ Type ret =
ScalarType.createTimeV2Type(Math.max(left.getScalarScale(),
right.getScalarScale()));
+ fn.setReturnType(ret);
+ fn.getReturnType().getPrimitiveType().setTimeType();
+ }
}
if (fnName.getFunction().equalsIgnoreCase("map")) {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/TimeDiff.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/TimeDiff.java
index 8004e254a2..2cff7c8886 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/TimeDiff.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/TimeDiff.java
@@ -21,6 +21,7 @@ import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.trees.expressions.Expression;
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
import
org.apache.doris.nereids.trees.expressions.functions.PropagateNullableOnDateLikeV2Args;
+import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeType;
@@ -28,6 +29,7 @@ import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.DateV2Type;
import org.apache.doris.nereids.types.TimeType;
import org.apache.doris.nereids.types.TimeV2Type;
+import org.apache.doris.nereids.types.coercion.AbstractDataType;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
@@ -46,8 +48,7 @@ public class TimeDiff extends ScalarFunction
FunctionSignature.ret(TimeType.INSTANCE).args(DateTimeType.INSTANCE,
DateTimeType.INSTANCE),
FunctionSignature.ret(TimeV2Type.INSTANCE).args(DateTimeV2Type.SYSTEM_DEFAULT,
DateV2Type.INSTANCE),
FunctionSignature.ret(TimeV2Type.INSTANCE).args(DateV2Type.INSTANCE,
DateTimeV2Type.SYSTEM_DEFAULT),
-
FunctionSignature.ret(TimeV2Type.INSTANCE).args(DateV2Type.INSTANCE,
DateV2Type.INSTANCE)
- );
+
FunctionSignature.ret(TimeV2Type.INSTANCE).args(DateV2Type.INSTANCE,
DateV2Type.INSTANCE));
/**
* constructor with 2 arguments.
@@ -74,4 +75,41 @@ public class TimeDiff extends ScalarFunction
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitTimeDiff(this, context);
}
+
+ @Override
+ public FunctionSignature computeSignature(FunctionSignature signature) {
+ signature = super.computeSignature(signature);
+ boolean useTimev2 = false;
+ int scale = 0;
+ if (getArgument(0).getDataType() instanceof DateTimeV2Type) {
+ DateTimeV2Type left = (DateTimeV2Type)
getArgument(0).getDataType();
+ scale = Math.max(scale, left.getScale());
+ useTimev2 = true;
+ }
+ if (getArgument(1).getDataType() instanceof DateTimeV2Type) {
+ DateTimeV2Type right = (DateTimeV2Type)
getArgument(1).getDataType();
+ scale = Math.max(scale, right.getScale());
+ useTimev2 = true;
+ }
+ if (useTimev2) {
+ signature = signature.withReturnType(TimeV2Type.of(scale));
+ }
+ return signature;
+ }
+
+ @Override
+ public List<AbstractDataType> expectedInputTypes() {
+ FunctionSignature signature = getSignature();
+ if (getArgument(0) instanceof StringLikeLiteral) {
+ StringLikeLiteral str = (StringLikeLiteral) getArgument(0);
+ DateTimeV2Type left =
DateTimeV2Type.forTypeFromString(str.getStringValue());
+ signature = signature.withArgumentType(0, left);
+ }
+ if (getArgument(1) instanceof StringLikeLiteral) {
+ StringLikeLiteral str = (StringLikeLiteral) getArgument(1);
+ DateTimeV2Type right =
DateTimeV2Type.forTypeFromString(str.getStringValue());
+ signature = signature.withArgumentType(1, right);
+ }
+ return signature.argumentsTypes;
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/TimeV2Type.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/TimeV2Type.java
index f798a9b3f7..ec625d0cd1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/TimeV2Type.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/TimeV2Type.java
@@ -17,6 +17,7 @@
package org.apache.doris.nereids.types;
+import org.apache.doris.catalog.ScalarType;
import org.apache.doris.catalog.Type;
import org.apache.doris.nereids.types.coercion.PrimitiveType;
@@ -28,13 +29,26 @@ public class TimeV2Type extends PrimitiveType {
public static final TimeV2Type INSTANCE = new TimeV2Type();
private static final int WIDTH = 8;
+ private final int scale;
+
+ private TimeV2Type(int scale) {
+ this.scale = scale;
+ }
private TimeV2Type() {
+ scale = 0;
}
@Override
public Type toCatalogDataType() {
- return Type.TIMEV2;
+ return ScalarType.createTimeV2Type(scale);
+ }
+
+ /**
+ * create TimeV2Type from scale
+ */
+ public static TimeV2Type of(int scale) {
+ return new TimeV2Type(scale);
}
@Override
@@ -46,4 +60,8 @@ public class TimeV2Type extends PrimitiveType {
public int width() {
return WIDTH;
}
+
+ public int getScale() {
+ return scale;
+ }
}
diff --git a/gensrc/proto/types.proto b/gensrc/proto/types.proto
index 7b74f6a804..af0f1fcc1d 100644
--- a/gensrc/proto/types.proto
+++ b/gensrc/proto/types.proto
@@ -109,6 +109,7 @@ message PGenericType {
QUANTILE_STATE = 34;
TIME = 35;
AGG_STATE = 36;
+ TIMEV2 = 37;
UNKNOWN = 999;
}
required TypeId id = 2;
diff --git a/regression-test/data/correctness/test_time_diff_microseconds.out
b/regression-test/data/correctness/test_time_diff_microseconds.out
new file mode 100644
index 0000000000..ce4c6c10f5
--- /dev/null
+++ b/regression-test/data/correctness/test_time_diff_microseconds.out
@@ -0,0 +1,29 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !select1 --
+67:19:00.0000
+24:00:00.5140
+-437:00:00.7683
+
+-- !select2 --
+48:00:00
+
+-- !select3 --
+48:00:00.114514
+
+-- !select4 --
+48:00:00.11400
+
+-- !select5 --
+67:19:00.0000
+24:00:00.5140
+-437:00:00.7683
+
+-- !select6 --
+48:00:00
+
+-- !select7 --
+48:00:00.114514
+
+-- !select8 --
+48:00:00.11400
+
diff --git a/regression-test/data/nereids_p0/test_char_implicit_cast.out
b/regression-test/data/nereids_p0/test_char_implicit_cast.out
index 59f5d47377..599dd6500d 100644
--- a/regression-test/data/nereids_p0/test_char_implicit_cast.out
+++ b/regression-test/data/nereids_p0/test_char_implicit_cast.out
@@ -6,10 +6,10 @@
7
-- !test_timediff_varchar --
--24:00:00
+-96:00:00
--- !test_timediff_char --
--24:00:00
+-- !test_timediff_varcharv2 --
+-96:00:00.3337
-- !test_money_format_varchar --
123,456.00
diff --git
a/regression-test/suites/correctness/test_time_diff_microseconds.groovy
b/regression-test/suites/correctness/test_time_diff_microseconds.groovy
new file mode 100644
index 0000000000..e754515250
--- /dev/null
+++ b/regression-test/suites/correctness/test_time_diff_microseconds.groovy
@@ -0,0 +1,95 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_time_diff_microseconds") {
+ sql """ DROP TABLE IF EXISTS tbl_time """
+ sql """
+ CREATE TABLE IF NOT EXISTS tbl_time (
+ `id` int(11) ,
+ `t1` DATETIMEV2(3) ,
+ `t2` DATETIMEV2(4)
+ )
+ UNIQUE KEY(`id`)
+ DISTRIBUTED BY HASH(`id`) BUCKETS 10
+ PROPERTIES (
+ "enable_unique_key_merge_on_write" = "true",
+ "replication_num" = "1"
+ );
+ """
+ sql """
+ INSERT INTO tbl_time VALUES(1,'0001-01-03 19:19:00.000000','0001-01-01
00:00:00.0000');
+ """
+
+ sql """
+ INSERT INTO tbl_time VALUES(2,'0001-01-02 00:00:00.514514','0001-01-01
00:00:00.000000');
+ """
+
+ sql """
+ INSERT INTO tbl_time VALUES(3,'0001-01-03 19:00:00.123123','0001-01-22
00:00:00.891312');
+ """
+
+ sql """set enable_nereids_planner=false"""
+
+ qt_select1 """
+ select timediff(t1,t2) from tbl_time order by id
+ """
+
+ qt_select2 """
+ select timediff(
+ cast('0001-01-03 11:45:14' as Date ) ,
+ cast('0001-01-01 00:00:00.000000' as Date));
+ """
+
+ qt_select3 """
+ select timediff(
+ cast('0001-01-03 00:00:00.114514' as Datetimev2(6) ) ,
+ cast('0001-01-01 00:00:00.000000' as Datetimev2(6) ));
+ """
+
+
+ qt_select4 """
+ select timediff(
+ cast('0001-01-03 00:00:00.114514' as Datetimev2(3) ) ,
+ cast('0001-01-01 00:00:00.000000' as Datetimev2(5) ));
+ """
+
+ sql """set enable_nereids_planner=true """
+ sql """set enable_fallback_to_original_planner=false"""
+
+ qt_select5 """
+ select timediff(t1,t2) from tbl_time order by id
+ """
+
+ qt_select6 """
+ select timediff(
+ cast('0001-01-03 11:45:14' as Date ) ,
+ cast('0001-01-01 00:00:00.000000' as Date));
+ """
+
+ qt_select7 """
+ select timediff(
+ cast('0001-01-03 00:00:00.114514' as Datetimev2(6) ) ,
+ cast('0001-01-01 00:00:00.000000' as Datetimev2(6) ));
+ """
+
+
+ qt_select8 """
+ select timediff(
+ cast('0001-01-03 00:00:00.114514' as Datetimev2(3) ) ,
+ cast('0001-01-01 00:00:00.000000' as Datetimev2(5) ));
+ """
+}
diff --git a/regression-test/suites/nereids_p0/test_char_implicit_cast.groovy
b/regression-test/suites/nereids_p0/test_char_implicit_cast.groovy
index b7e23812e8..415a808365 100644
--- a/regression-test/suites/nereids_p0/test_char_implicit_cast.groovy
+++ b/regression-test/suites/nereids_p0/test_char_implicit_cast.groovy
@@ -20,8 +20,8 @@ suite("test_char_implicit_cast") {
sql "SET enable_fallback_to_original_planner=false"
qt_test_dayofweek_varchar 'select dayofweek("2012-12-01");'
qt_test_dayofweek_char 'select dayofweek(cast("2012-12-01" as char(16)));'
- qt_test_timediff_varchar 'select timediff("2010-01-01 01:00:00",
"2010-01-02 01:00:00");'
- qt_test_timediff_char 'select timediff("2010-01-01 01:00:00",
cast("2010-01-02 01:00:00" as char));'
+ qt_test_timediff_varchar 'select timediff("2010-01-01 01:00:00",
"2010-01-05 01:00:00");'
+ qt_test_timediff_varcharv2 'select timediff("2010-01-01 01:00:00.123",
"2010-01-05 01:00:00.4567");'
qt_test_money_format_varchar 'select money_format("123456");'
qt_test_money_format_char 'select money_format(cast("123456" as char));'
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]