Skip to content

Commit c12644f

Browse files
committed
Make it possible to backup out-of-range decimal values (#26510)
1 parent 305a438 commit c12644f

File tree

2 files changed

+146
-3
lines changed

2 files changed

+146
-3
lines changed

ydb/core/tx/datashard/type_serialization.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ TString DecimalToString(const std::pair<ui64, i64>& loHi, const NScheme::TTypeIn
1515
using namespace NYql::NDecimal;
1616

1717
TInt128 val = FromHalfs(loHi.first, loHi.second);
18-
return ToString(val, typeInfo.GetDecimalType().GetPrecision(), typeInfo.GetDecimalType().GetScale());
18+
const char* result = ToString(val, MaxPrecision /*typeInfo.GetDecimalType().GetPrecision()*/, typeInfo.GetDecimalType().GetScale());
19+
Y_ENSURE(result);
20+
21+
return result;
1922
}
2023

2124
TString DyNumberToString(TStringBuf data) {
@@ -36,11 +39,15 @@ TString PgToString(TStringBuf data, const NScheme::TTypeInfo& typeInfo) {
3639
}
3740

3841
bool DecimalToStream(const std::pair<ui64, i64>& loHi, IOutputStream& out, TString& err, const NScheme::TTypeInfo& typeInfo) {
39-
Y_UNUSED(err);
4042
using namespace NYql::NDecimal;
4143

4244
TInt128 val = FromHalfs(loHi.first, loHi.second);
43-
out << ToString(val, typeInfo.GetDecimalType().GetPrecision(), typeInfo.GetDecimalType().GetScale());
45+
const char* result = ToString(val, MaxPrecision /*typeInfo.GetDecimalType().GetPrecision()*/, typeInfo.GetDecimalType().GetScale());
46+
if (!result) [[unlikely]] {
47+
err = "Invalid Decimal binary representation";
48+
return false;
49+
}
50+
out << result;
4451
return true;
4552
}
4653

ydb/core/tx/schemeshard/ut_export/ut_export.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2788,4 +2788,140 @@ attributes {
27882788
)",
27892789
}, request, Ydb::StatusIds::SUCCESS, "/MyRoot", false, "", "", {}, true);
27902790
}
2791+
2792+
Y_UNIT_TEST(DecimalOutOfRange) {
2793+
TTestBasicRuntime runtime;
2794+
TTestEnv env(runtime);
2795+
ui64 txId = 100;
2796+
2797+
TestCreateTable(runtime, ++txId, "/MyRoot", R"(
2798+
Name: "Table1"
2799+
Columns { Name: "key" Type: "Uint64" }
2800+
Columns { Name: "value" Type: "Decimal" }
2801+
KeyColumnNames: ["key"]
2802+
)");
2803+
env.TestWaitNotification(runtime, txId);
2804+
2805+
// Write a normal decimal value
2806+
// 10.0^13-1 (scale 9) = 0x21e19e0c9ba76a53600
2807+
{
2808+
ui64 key = 1u;
2809+
std::pair<ui64, i64> value = { 0x19e0c9ba76a53600ULL, 0x21eULL };
2810+
UploadRow(runtime, "/MyRoot/Table1", 0, {1}, {2}, {TCell::Make(key)}, {TCell::Make(value)});
2811+
}
2812+
// Write a decimal value that is out of range for precision 22
2813+
// 10.0^13 (scale 9) = 10^22 = 0x21e19e0c9bab2400000
2814+
{
2815+
ui64 key = 2u;
2816+
std::pair<ui64, i64> value = { 0x19e0c9bab2400000ULL, 0x21eULL };
2817+
UploadRow(runtime, "/MyRoot/Table1", 0, {1}, {2}, {TCell::Make(key)}, {TCell::Make(value)});
2818+
}
2819+
2820+
TPortManager portManager;
2821+
const ui16 port = portManager.GetPort();
2822+
2823+
TS3Mock s3Mock({}, TS3Mock::TSettings(port));
2824+
UNIT_ASSERT(s3Mock.Start());
2825+
2826+
TestExport(runtime, ++txId, "/MyRoot", Sprintf(R"(
2827+
ExportToS3Settings {
2828+
endpoint: "localhost:%d"
2829+
scheme: HTTP
2830+
items {
2831+
source_path: "/MyRoot/Table1"
2832+
destination_prefix: "Backup1"
2833+
}
2834+
}
2835+
)", port));
2836+
env.TestWaitNotification(runtime, txId);
2837+
2838+
TestGetExport(runtime, txId, "/MyRoot", Ydb::StatusIds::SUCCESS);
2839+
2840+
{
2841+
auto it = s3Mock.GetData().find("/Backup1/data_00.csv");
2842+
UNIT_ASSERT(it != s3Mock.GetData().end());
2843+
UNIT_ASSERT_STRINGS_EQUAL(it->second,
2844+
"1,9999999999999\n"
2845+
"2,10000000000000\n");
2846+
}
2847+
2848+
TestImport(runtime, ++txId, "/MyRoot", Sprintf(R"(
2849+
ImportFromS3Settings {
2850+
endpoint: "localhost:%d"
2851+
scheme: HTTP
2852+
items {
2853+
source_prefix: "Backup1"
2854+
destination_path: "/MyRoot/Table2"
2855+
}
2856+
}
2857+
)", port));
2858+
env.TestWaitNotification(runtime, txId);
2859+
2860+
TestGetImport(runtime, txId, "/MyRoot", Ydb::StatusIds::SUCCESS);
2861+
2862+
TestExport(runtime, ++txId, "/MyRoot", Sprintf(R"(
2863+
ExportToS3Settings {
2864+
endpoint: "localhost:%d"
2865+
scheme: HTTP
2866+
items {
2867+
source_path: "/MyRoot/Table2"
2868+
destination_prefix: "Backup2"
2869+
}
2870+
}
2871+
)", port));
2872+
env.TestWaitNotification(runtime, txId);
2873+
2874+
TestGetExport(runtime, txId, "/MyRoot", Ydb::StatusIds::SUCCESS);
2875+
2876+
// Note: out-of-range values are restored as inf
2877+
{
2878+
auto it = s3Mock.GetData().find("/Backup2/data_00.csv");
2879+
UNIT_ASSERT(it != s3Mock.GetData().end());
2880+
UNIT_ASSERT_STRINGS_EQUAL(it->second,
2881+
"1,9999999999999\n"
2882+
"2,inf\n");
2883+
}
2884+
}
2885+
2886+
Y_UNIT_TEST(CorruptedDecimalValue) {
2887+
TTestBasicRuntime runtime;
2888+
TTestEnv env(runtime);
2889+
ui64 txId = 100;
2890+
2891+
TestCreateTable(runtime, ++txId, "/MyRoot", R"(
2892+
Name: "Table1"
2893+
Columns { Name: "key" Type: "Uint64" }
2894+
Columns { Name: "value" Type: "Decimal" }
2895+
KeyColumnNames: ["key"]
2896+
)");
2897+
env.TestWaitNotification(runtime, txId);
2898+
2899+
// Write a decimal value that is way out of range for max precision 35
2900+
// 10^38 = 0x4b3b4ca85a86c47a098a224000000000
2901+
{
2902+
ui64 key = 1u;
2903+
std::pair<ui64, i64> value = { 0x098a224000000000ULL, 0x4b3b4ca85a86c47aULL };
2904+
UploadRow(runtime, "/MyRoot/Table1", 0, {1}, {2}, {TCell::Make(key)}, {TCell::Make(value)});
2905+
}
2906+
2907+
TPortManager portManager;
2908+
const ui16 port = portManager.GetPort();
2909+
2910+
TS3Mock s3Mock({}, TS3Mock::TSettings(port));
2911+
UNIT_ASSERT(s3Mock.Start());
2912+
2913+
TestExport(runtime, ++txId, "/MyRoot", Sprintf(R"(
2914+
ExportToS3Settings {
2915+
endpoint: "localhost:%d"
2916+
scheme: HTTP
2917+
items {
2918+
source_path: "/MyRoot/Table1"
2919+
destination_prefix: "Backup1"
2920+
}
2921+
}
2922+
)", port));
2923+
env.TestWaitNotification(runtime, txId);
2924+
2925+
TestGetExport(runtime, txId, "/MyRoot", Ydb::StatusIds::CANCELLED);
2926+
}
27912927
}

0 commit comments

Comments
 (0)