Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement TimeToSec function push down #5235

Merged
merged 6 commits into from
Jul 7, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion dbms/src/Common/MyDuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,10 @@ String MyDuration::toString() const
auto frac_str = fmt::format("{:06}", microsecond);
return fmt::format(fmt_str, sign > 0 ? "" : "-", hour, minute, second, frac_str);
}
} // namespace DB

UInt64 calcSeconds(int hour, int minute, int second)
hey-kong marked this conversation as resolved.
Show resolved Hide resolved
{
return hour * 3600 + minute * 60 + second;
}

} // namespace DB
4 changes: 4 additions & 0 deletions dbms/src/Common/MyDuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,8 @@ class MyDuration

String toString() const;
};

// returns seconds since '00:00:00'
UInt64 calcSeconds(int hour, int minute, int second);

} // namespace DB
2 changes: 1 addition & 1 deletion dbms/src/Flash/Coprocessor/DAGUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ const std::unordered_map<tipb::ScalarFuncSig, String> scalar_func_map({
{tipb::ScalarFuncSig::Quarter, "toQuarter"},

//{tipb::ScalarFuncSig::SecToTime, "cast"},
//{tipb::ScalarFuncSig::TimeToSec, "cast"},
{tipb::ScalarFuncSig::TimeToSec, "tidbTimeToSec"},
//{tipb::ScalarFuncSig::TimestampAdd, "cast"},
{tipb::ScalarFuncSig::ToDays, "tidbToDays"},
{tipb::ScalarFuncSig::ToSeconds, "tidbToSeconds"},
Expand Down
69 changes: 69 additions & 0 deletions dbms/src/Functions/FunctionsDuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,57 @@ void FunctionDurationSplit<Impl>::executeImpl(Block & block, const ColumnNumbers
ErrorCodes::ILLEGAL_COLUMN);
};

template <typename Impl>
DataTypePtr FunctionMyDurationToSec<Impl>::getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const
{
if (!arguments[0].type->isMyTime())
bestwoody marked this conversation as resolved.
Show resolved Hide resolved
{
throw Exception(
fmt::format("Illegal type {} of first argument of function {}", arguments[0].type->getName(), getName()),
hey-kong marked this conversation as resolved.
Show resolved Hide resolved
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
}
return std::make_shared<DataTypeInt64>();
}

template <typename Impl>
void FunctionMyDurationToSec<Impl>::executeImpl(Block & block, const ColumnNumbers & arguments, size_t result) const
{
const auto * from_type = checkAndGetDataType<DataTypeMyDuration>(block.getByPosition(arguments[0]).type.get());
if (from_type == nullptr)
{
throw Exception(
fmt::format(
"Illegal column {} of first argument of function {}",
hey-kong marked this conversation as resolved.
Show resolved Hide resolved
block.getByPosition(arguments[0]).column->getName(),
name),
ErrorCodes::ILLEGAL_COLUMN);
}

using FromFieldType = typename DataTypeMyDuration::FieldType;
const auto * col_from = checkAndGetColumn<ColumnVector<FromFieldType>>(block.getByPosition(arguments[0]).column.get());
if (col_from != nullptr)
{
const typename ColumnVector<FromFieldType>::Container & vec_from = col_from->getData();
const size_t size = vec_from.size();
auto col_to = ColumnVector<Int64>::create(size);
typename ColumnVector<Int64>::Container & vec_to = col_to->getData();

for (size_t i = 0; i < vec_from.size(); ++i)
hey-kong marked this conversation as resolved.
Show resolved Hide resolved
{
MyDuration val(vec_from[i], from_type->getFsp());
vec_to[i] = Impl::apply(val);
}
block.getByPosition(result).column = std::move(col_to);
}
else
throw Exception(
fmt::format(
"Illegal column {} of first argument of function {}",
hey-kong marked this conversation as resolved.
Show resolved Hide resolved
block.getByPosition(arguments[0]).column->getName(),
name),
ErrorCodes::ILLEGAL_COLUMN);
}

struct DurationSplitHourImpl
{
static constexpr auto name = "hour";
Expand Down Expand Up @@ -133,11 +184,27 @@ struct DurationSplitMicroSecondImpl
}
};

struct TiDBTimeToSecTransformerImpl
{
static constexpr auto name = "tidbTimeToSec";
static Int64 apply(const MyDuration & val)
{
Int64 sign = 1;
if (val.isNeg())
{
sign = -1;
}
return sign * calcSeconds(val.hours(), val.minutes(), val.seconds());
}
};

using FunctionDurationHour = FunctionDurationSplit<DurationSplitHourImpl>;
using FunctionDurationMinute = FunctionDurationSplit<DurationSplitMinuteImpl>;
using FunctionDurationSecond = FunctionDurationSplit<DurationSplitSecondImpl>;
using FunctionDurationMicroSecond = FunctionDurationSplit<DurationSplitMicroSecondImpl>;

using FunctionToTiDBTimeToSec = FunctionMyDurationToSec<TiDBTimeToSecTransformerImpl>;

void registerFunctionsDuration(FunctionFactory & factory)
{
factory.registerFunction<FunctionConvertDurationFromNanos>();
Expand All @@ -146,5 +213,7 @@ void registerFunctionsDuration(FunctionFactory & factory)
factory.registerFunction<FunctionDurationMinute>();
factory.registerFunction<FunctionDurationSecond>();
factory.registerFunction<FunctionDurationMicroSecond>();

factory.registerFunction<FunctionToTiDBTimeToSec>();
}
} // namespace DB
19 changes: 19 additions & 0 deletions dbms/src/Functions/FunctionsDuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,23 @@ class FunctionDurationSplit : public IFunction
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result) const override;
};

template <typename Impl>
class FunctionMyDurationToSec : public IFunction
{
public:
static constexpr auto name = Impl::name;

static FunctionPtr create(const Context &) { return std::make_shared<FunctionMyDurationToSec>(); };

String getName() const override { return name; }

size_t getNumberOfArguments() const override { return 1; }

bool useDefaultImplementationForConstants() const override { return true; }

DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override;

void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result) const override;
};

} // namespace DB
80 changes: 80 additions & 0 deletions dbms/src/Functions/tests/gtest_duration_pushdown.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,5 +166,85 @@ try
ASSERT_COLUMN_EQ(microSecond_out, executeFunction("microSecond", input4));
}
CATCH

TEST_F(DurationPushDown, timeToSecPushDownTest)
try
{
ColumnWithTypeAndName input(
createColumn<Nullable<DataTypeMyDuration::FieldType>>({(838 * 3600 + 59 * 60 + 59) * 1000000000L + 999999000L,
-(838 * 3600 + 59 * 60 + 59) * 1000000000L - 123456000L,
0,
(1 * 3600 + 2 * 60 + 3) * 1000000000L + 4000L})
.column,
makeNullable(std::make_shared<DataTypeMyDuration>(6)),
"input");
auto second_output = createColumn<Nullable<Int64>>({3020399, -3020399, 0, 3723});
ASSERT_COLUMN_EQ(second_output, executeFunction("tidbTimeToSec", input));

// Test Overflow
ColumnWithTypeAndName input2(
createColumn<Nullable<DataTypeMyDuration::FieldType>>({(838 * 3600 + 59 * 60 + 59) * 1000000000L + 999999000L + 1000L}).column,
makeNullable(std::make_shared<DataTypeMyDuration>(6)),
"result");
try
{
auto result = executeFunction("tidbTimeToSec", input2);
FAIL() << "Expected overflow";
}
catch (DB::Exception & e)
{
ASSERT_EQ(e.message(), std::string("nanos must >= -3020399999999000 and <= 3020399999999000"));
}
catch (...)
{
FAIL() << "Expected overflow";
};

ColumnWithTypeAndName input3(
createColumn<Nullable<DataTypeMyDuration::FieldType>>({-(838 * 3600 + 59 * 60 + 59) * 1000000000L - 999999000L - 1000L}).column,
makeNullable(std::make_shared<DataTypeMyDuration>(6)),
"result");
try
{
auto result = executeFunction("tidbTimeToSec", input3);
FAIL() << "Expected overflow";
}
catch (DB::Exception & e)
{
ASSERT_EQ(e.message(), std::string("nanos must >= -3020399999999000 and <= 3020399999999000"));
}
catch (...)
{
FAIL() << "Expected overflow";
};

// Random Test
constexpr int rowNum = 1000;
auto dur_column = ColumnVector<Int64>::create();
auto & dur_data = dur_column->getData();
auto second_column = ColumnVector<Int64>::create();
auto & second_data = second_column->getData();
dur_data.resize(rowNum);
second_data.resize(rowNum);

std::random_device rd;
std::default_random_engine gen = std::default_random_engine(rd());
std::uniform_int_distribution<int> sign_dis(0, 1), hour_dis(0, 838), minute_dis(0, 59), second_dis(0, 59), microSecond_dis(0, 999999);
for (int i = 0; i < rowNum; i++)
hey-kong marked this conversation as resolved.
Show resolved Hide resolved
{
auto sign = (sign_dis(gen) == 0) ? 1 : -1;
auto hour = hour_dis(gen);
auto minute = minute_dis(gen);
auto second = second_dis(gen);
auto microSecond = microSecond_dis(gen);
dur_data[i] = sign * ((hour * 3600 + minute * 60 + second) * 1000000000L + microSecond * 1000L);
second_data[i] = sign * (hour * 3600 + minute * 60 + second);
}

ColumnWithTypeAndName input4(std::move(dur_column), std::make_shared<DataTypeMyDuration>(6), "duration");
ColumnWithTypeAndName second_out(std::move(second_column), std::make_shared<DataTypeInt64>(), "time_to_sec");
ASSERT_COLUMN_EQ(second_out, executeFunction("tidbTimeToSec", input4));
}
CATCH
} // namespace tests
} // namespace DB
8 changes: 8 additions & 0 deletions tests/fullstack-test/expr/duration_pushdown.test
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ mysql> use test; set tidb_enforce_mpp=1; set tidb_isolation_read_engines='tiflas
# | 123500 |
# +----------------+

mysql> use test; set tidb_enforce_mpp=1; set tidb_isolation_read_engines='tiflash'; select time_to_sec(a) from t;
+----------------+
| time_to_sec(a) |
+----------------+
| 2520610 |
| -2520610 |
+----------------+


mysql> drop table if exists test.time_test;
mysql> create table test.time_test(id int(11),v1 time(3) not null, v2 time(3));
Expand Down