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

Fix cast decimal as string (#1256) #1281

Merged
merged 1 commit into from
Dec 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
394 changes: 359 additions & 35 deletions dbms/src/Common/MyTime.cpp

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dbms/src/Common/MyTime.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ struct MyDate : public MyTimeBase
String toString() const { return dateFormat("%Y-%m-%d"); }
};

Field parseMyDateTime(const String & str);
Field parseMyDateTime(const String & str, int8_t fsp = 6);

void convertTimeZone(UInt64 from_time, UInt64 & to_time, const DateLUTImpl & time_zone_from, const DateLUTImpl & time_zone_to);

Expand Down
8 changes: 7 additions & 1 deletion dbms/src/Common/TiFlashException.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,13 @@ namespace DB
E(Internal, "Encryption internal error.", \
"Please contact with developer, \n" \
"better providing information about your cluster(log, topology information etc.).", \
"");)
"");) \
C(MPP, \
E(Internal, "MPP internal error.", \
"Please contact with developer, \n" \
"better providing information about your cluster(log, topology information etc.).", \
"");) \
C(Types, E(Truncated, "Data is truncated during conversion.", "", ""); E(WrongValue, "Input value is in wrong format", "", "");)


/// TiFlashError is core struct of standard error,
Expand Down
3 changes: 3 additions & 0 deletions dbms/src/Common/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,6 @@ target_link_libraries (persisted_container clickhouse_common_io)

add_executable(decimal_test_decimal_type gtest_decimal_type.cpp)
target_link_libraries(decimal_test_decimal_type clickhouse_common_io gtest_main)

add_executable(mytime_test gtest_mytime.cpp)
target_link_libraries(mytime_test clickhouse_common_io clickhouse_functions gtest_main)
157 changes: 157 additions & 0 deletions dbms/src/Common/tests/gtest_mytime.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#include <Common/Exception.h>
#include <Common/MyTime.h>
#include <DataTypes/DataTypeMyDateTime.h>
#include <gtest/gtest.h>

#include <iostream>
#include <string>
#include <tuple>
#include <vector>

namespace DB
{
namespace tests
{

class TestMyTime : public testing::Test
{
protected:
virtual void SetUp() override {}
virtual void TearDown() override {}

public:
static void checkParseMyDateTime(const std::string & str, const std::string & expected, const DataTypeMyDateTime & type)
{
try
{
UInt64 res = parseMyDateTime(str, type.getFraction()).template safeGet<UInt64>();
MyDateTime datetime(res);
std::string actual = datetime.toString(type.getFraction());
EXPECT_EQ(actual, expected) << "Original datetime string: " << str;
}
catch (...)
{
std::cerr << "Error occurs when parsing: \"" << str << "\"" << std::endl;
throw;
}
}

static void checkParseMyDateTime(const std::string & str, MyDateTime & expected, const DataTypeMyDateTime & type)
{
try
{
UInt64 res = parseMyDateTime(str, type.getFraction()).template safeGet<UInt64>();
MyDateTime source(res);
EXPECT_EQ(source.year, expected.year) << "Original datetime string: " << str;
EXPECT_EQ(source.month, expected.month) << "Original datetime string: " << str;
EXPECT_EQ(source.day, expected.day) << "Original datetime string: " << str;
EXPECT_EQ(source.hour, expected.hour) << "Original datetime string: " << str;
EXPECT_EQ(source.minute, expected.minute) << "Original datetime string: " << str;
EXPECT_EQ(source.second, expected.second) << "Original datetime string: " << str;
EXPECT_EQ(source.micro_second, expected.micro_second) << "Original datetime string: " << str;
}
catch (...)
{
std::cerr << "Error occurs when parsing: \"" << str << "\"" << std::endl;
throw;
}
}
};

TEST_F(TestMyTime, ParseMyDateTimeWithFraction)
try
{
std::vector<std::tuple<std::string, std::string>> cases_with_fsp{
{"2020-12-10 11:11:11.123456", "2020-12-10 11:11:11.123456"}, // YYYY-MM-DD HH:MM:SS.mmmmmm
{"00-00-00 00:00:00.123", "2000-00-00 00:00:00.123000"},
{"1701020304.1", "2017-01-02 03:04:01.000000"},
{"1701020302.11", "2017-01-02 03:02:11.000000"},
{"170102037.11", "2017-01-02 03:07:11.000000"},
{"2018.01.01", "2018-01-01 00:00:00.000000"},
{"2020.10.10 10.10.10", "2020-10-10 10:10:10.000000"},
{"2020-10-10 10-10.10", "2020-10-10 10:10:10.000000"},
{"2020-10-10 10.10", "2020-10-10 10:10:00.000000"},
{"2018.01.01", "2018-01-01 00:00:00.000000"},
};
DataTypeMyDateTime type_with_fraction(6);
for (auto & [str, expected] : cases_with_fsp)
{
checkParseMyDateTime(str, expected, type_with_fraction);
}
}
catch (Exception & e)
{
std::cerr << e.displayText() << std::endl;
GTEST_FAIL();
}

TEST_F(TestMyTime, ParseMyDateTimeWithoutFraction)
try
{
std::vector<std::tuple<std::string, std::string>> cases_without_fsp{
{"2012-12-31 11:30:45", "2012-12-31 11:30:45"},
{"0000-00-00 00:00:00", "0000-00-00 00:00:00"},
{"0001-01-01 00:00:00", "0001-01-01 00:00:00"},
{"00-12-31 11:30:45", "2000-12-31 11:30:45"},
{"12-12-31 11:30:45", "2012-12-31 11:30:45"},
{"2012-12-31", "2012-12-31 00:00:00"},
{"20121231", "2012-12-31 00:00:00"},
{"121231", "2012-12-31 00:00:00"},
{"2012^12^31 11+30+45", "2012-12-31 11:30:45"},
{"2012^12^31T11+30+45", "2012-12-31 11:30:45"},
{"2012-2-1 11:30:45", "2012-02-01 11:30:45"},
{"12-2-1 11:30:45", "2012-02-01 11:30:45"},
{"20121231113045", "2012-12-31 11:30:45"},
{"121231113045", "2012-12-31 11:30:45"},
{"2012-02-29", "2012-02-29 00:00:00"},
{"00-00-00", "0000-00-00 00:00:00"},
{"11111111111", "2011-11-11 11:11:01"},
{"1701020301.", "2017-01-02 03:01:00"},
{"170102036", "2017-01-02 03:06:00"},
{"170102039.", "2017-01-02 03:09:00"},
{"2018-01-01 18", "2018-01-01 18:00:00"},
{"18-01-01 18", "2018-01-01 18:00:00"},
{"2018.01.01 00:00:00", "2018-01-01 00:00:00"},
{"2018/01/01-00:00:00", "2018-01-01 00:00:00"},
{"4710072", "2047-10-07 02:00:00"},
};
DataTypeMyDateTime type_without_fraction(0);
for (auto & [str, expected] : cases_without_fsp)
{
checkParseMyDateTime(str, expected, type_without_fraction);
}
}
catch (Exception & e)
{
std::cerr << e.displayText() << std::endl;
GTEST_FAIL();
}

TEST_F(TestMyTime, ParseMyDateTimeWithTimezone)
try
{
std::vector<std::tuple<std::string, MyDateTime>> cases{
{"2006-01-02T15:04:05Z", MyDateTime(2006, 1, 2, 15, 4, 5, 0)},
{"2020-10-21T16:05:10Z", MyDateTime(2020, 10, 21, 16, 5, 10, 0)},
{"2020-10-21T16:05:10.50+08", MyDateTime(2020, 10, 21, 8, 5, 10, 500 * 1000)},
{"2020-10-21T16:05:10.50-0700", MyDateTime(2020, 10, 21, 23, 5, 10, 500 * 1000)},
{"2020-10-21T16:05:10.50+09:00", MyDateTime(2020, 10, 21, 7, 5, 10, 500 * 1000)},
{"2006-01-02T15:04:05+09:00", MyDateTime(2006, 1, 2, 6, 4, 5, 0)},
{"2006-01-02T15:04:05-02:00", MyDateTime(2006, 1, 2, 17, 4, 5, 0)},
{"2006-01-02T15:04:05-14:00", MyDateTime(2006, 1, 3, 5, 4, 5, 0)},
};
DataTypeMyDateTime type(6);
for (auto & [str, expected] : cases)
{
checkParseMyDateTime(str, expected, type);
}
}
catch (Exception & e)
{
std::cerr << e.displayText() << std::endl;
GTEST_FAIL();
}

} // namespace tests

} // namespace DB
11 changes: 4 additions & 7 deletions dbms/src/DataTypes/DataTypeMyDateTime.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
#include <IO/ReadHelpers.h>
#include <IO/WriteHelpers.h>

#include <Columns/ColumnsNumber.h>
#include <Common/typeid_cast.h>
#include <DataTypes/DataTypeFactory.h>
#include <DataTypes/DataTypeMyDateTime.h>
#include <common/DateLUT.h>

#include <IO/Operators.h>
#include <IO/ReadHelpers.h>
#include <IO/WriteBufferFromString.h>

#include <IO/WriteHelpers.h>
#include <Parsers/ASTLiteral.h>
#include <common/DateLUT.h>


namespace DB
Expand Down Expand Up @@ -102,7 +99,7 @@ bool DataTypeMyDateTime::equals(const IDataType & rhs) const
{
/// DateTime with different timezones are equal, because:
/// "all types with different time zones are equivalent and may be used interchangingly."
return typeid(rhs) == typeid(*this);
return typeid(rhs) == typeid(*this) && getFraction() == dynamic_cast<const DataTypeMyDateTime *>(&rhs)->getFraction();
}


Expand Down
34 changes: 34 additions & 0 deletions dbms/src/Debug/dbgFuncCoprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,22 @@ std::unordered_map<String, tipb::ScalarFuncSig> func_name_to_sig({
{"cast_decimal_decimal", tipb::ScalarFuncSig::CastDecimalAsDecimal},
{"cast_time_decimal", tipb::ScalarFuncSig::CastTimeAsDecimal},
{"cast_string_decimal", tipb::ScalarFuncSig::CastStringAsDecimal},
{"cast_int_string", tipb::ScalarFuncSig::CastIntAsString},
{"cast_real_string", tipb::ScalarFuncSig::CastRealAsString},
{"cast_decimal_string", tipb::ScalarFuncSig::CastDecimalAsString},
{"cast_time_string", tipb::ScalarFuncSig::CastTimeAsString},
{"cast_string_string", tipb::ScalarFuncSig::CastStringAsString},
{"cast_int_date", tipb::ScalarFuncSig::CastIntAsTime},
{"cast_real_date", tipb::ScalarFuncSig::CastRealAsTime},
{"cast_decimal_date", tipb::ScalarFuncSig::CastDecimalAsTime},
{"cast_time_date", tipb::ScalarFuncSig::CastTimeAsTime},
{"cast_string_date", tipb::ScalarFuncSig::CastStringAsTime},
{"cast_int_datetime", tipb::ScalarFuncSig::CastIntAsTime},
{"cast_real_datetime", tipb::ScalarFuncSig::CastRealAsTime},
{"cast_decimal_datetime", tipb::ScalarFuncSig::CastDecimalAsTime},
{"cast_time_datetime", tipb::ScalarFuncSig::CastTimeAsTime},
{"cast_string_datetime", tipb::ScalarFuncSig::CastStringAsTime},

});

void compileExpr(const DAGSchema & input, ASTPtr ast, tipb::Expr * expr, std::unordered_set<String> & referred_columns,
Expand Down Expand Up @@ -399,6 +415,24 @@ void compileExpr(const DAGSchema & input, ASTPtr ast, tipb::Expr * expr, std::un
expr->set_sig(tipb::ScalarFuncSig::DateFormatSig);
expr->mutable_field_type()->set_tp(TiDB::TypeString);
break;
case tipb::ScalarFuncSig::CastIntAsTime:
case tipb::ScalarFuncSig::CastRealAsTime:
case tipb::ScalarFuncSig::CastTimeAsTime:
case tipb::ScalarFuncSig::CastDecimalAsTime:
case tipb::ScalarFuncSig::CastStringAsTime:
{
expr->set_sig(it_sig->second);
auto * ft = expr->mutable_field_type();
if (it_sig->first.find("datetime"))
{
ft->set_tp(TiDB::TypeDatetime);
}
else
{
ft->set_tp(TiDB::TypeDate);
}
break;
}
default:
{
expr->set_sig(it_sig->second);
Expand Down
1 change: 1 addition & 0 deletions dbms/src/Flash/Coprocessor/DAGExpressionAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ String DAGExpressionAnalyzer::convertToUInt8(ExpressionActionsPtr & actions, con
{
/// use tidb_cast to make it compatible with TiDB
tipb::FieldType field_type;
// TODO: Use TypeDouble as return type, to be compatible with TiDB
field_type.set_tp(TiDB::TypeLongLong);
tipb::Expr type_expr;
constructStringLiteralTiExpr(type_expr, "Nullable(Int64)");
Expand Down
9 changes: 9 additions & 0 deletions dbms/src/Functions/FunctionsConversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,15 @@ struct FormatImpl<DataTypeMyDateTime>
}
};

template <typename DecimalType>
struct FormatImpl<DataTypeDecimal<DecimalType>>
{
static void execute(const typename DataTypeDecimal<DecimalType>::FieldType v, WriteBuffer & wb, const DataTypeDecimal<DecimalType> * tp, const DateLUTImpl *)
{
writeText(v, tp->getScale(), wb);
}
};

template <typename FieldType>
struct FormatImpl<DataTypeEnum<FieldType>>
{
Expand Down
Loading