Skip to content

Commit

Permalink
Implement TimeToSec function push down (#5235)
Browse files Browse the repository at this point in the history
close #5116
  • Loading branch information
hey-kong authored Jul 7, 2022
1 parent fe2b539 commit d9b7086
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 2 deletions.
2 changes: 1 addition & 1 deletion dbms/src/Common/MyDuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,4 @@ 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
} // 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())
{
throw Exception(
fmt::format("Illegal type {} of the first argument of function {}", arguments[0].type->getName(), getName()),
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 the first argument of function {}",
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 < size; ++i)
{
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 the first argument of function {}",
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 * (val.hours() * 3600 + val.minutes() * 60 + 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)
{
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

0 comments on commit d9b7086

Please sign in to comment.