summaryrefslogtreecommitdiffstats
path: root/library/cpp
diff options
context:
space:
mode:
authorudovichenko-r <[email protected]>2025-03-27 09:42:31 +0300
committerudovichenko-r <[email protected]>2025-03-27 09:56:18 +0300
commit86e72e8f45d1eb0948a9a20f82b3f81a03df77b5 (patch)
treed84cf7111d81a5aa191f7f308dccbe115b4ed3ee /library/cpp
parent54394ecb8d819f075dddd15ed8cdb4fb822d66cd (diff)
Fix YT deserialization of schemas with Decimal columns having zero scale
commit_hash:04525d1d60a5ace05ce17e9c8aebdd3b4b750eef
Diffstat (limited to 'library/cpp')
-rw-r--r--library/cpp/type_info/type_io.cpp33
-rw-r--r--library/cpp/type_info/ut/type_deserialize.cpp34
2 files changed, 56 insertions, 11 deletions
diff --git a/library/cpp/type_info/type_io.cpp b/library/cpp/type_info/type_io.cpp
index 12f0058fed1..f397466abce 100644
--- a/library/cpp/type_info/type_io.cpp
+++ b/library/cpp/type_info/type_io.cpp
@@ -13,6 +13,8 @@
#include <util/generic/vector.h>
#include <util/generic/scope.h>
+#include <optional>
+
namespace NTi::NIo {
namespace {
class TYsonDeserializer: private TNonCopyable {
@@ -61,7 +63,8 @@ namespace NTi::NIo {
const TType *Key, *Value;
};
struct TDecimalData {
- ui8 Precision, Scale;
+ std::optional<ui8> Precision;
+ std::optional<ui8> Scale;
};
using TTypeData = std::variant<
std::monostate,
@@ -162,11 +165,19 @@ namespace NTi::NIo {
}
} else if (mapKey == "precision") {
if (std::holds_alternative<std::monostate>(data)) {
- data = TDecimalData{ReadSmallInt(R"("precision")"), 0};
+ const auto precision = ReadSmallInt(R"("precision")");
+ if (0 == precision) {
+ ythrow TDeserializationException() << R"(invalid zero "precision")";
+ }
+ data = TDecimalData{precision, std::nullopt};
} else if (std::holds_alternative<TDecimalData>(data)) {
auto& decimalData = std::get<TDecimalData>(data);
- if (decimalData.Precision == 0) {
- decimalData.Precision = ReadSmallInt(R"("precision")");
+ if (!decimalData.Precision.has_value()) {
+ const auto precision = ReadSmallInt(R"("precision")");
+ if (0 == precision) {
+ ythrow TDeserializationException() << R"(invalid zero "precision")";
+ }
+ decimalData.Precision = precision;
} else {
ythrow TDeserializationException() << R"(duplicate key "precision")";
}
@@ -175,10 +186,10 @@ namespace NTi::NIo {
}
} else if (mapKey == "scale") {
if (std::holds_alternative<std::monostate>(data)) {
- data = TDecimalData{0, ReadSmallInt(R"("scale")")};
+ data = TDecimalData{std::nullopt, ReadSmallInt(R"("scale")")};
} else if (std::holds_alternative<TDecimalData>(data)) {
auto& decimalData = std::get<TDecimalData>(data);
- if (decimalData.Scale == 0) {
+ if (!decimalData.Scale.has_value()) {
decimalData.Scale = ReadSmallInt(R"("scale")");
} else {
ythrow TDeserializationException() << R"(duplicate key "scale")";
@@ -272,15 +283,15 @@ namespace NTi::NIo {
auto& decimalData = std::get<TDecimalData>(data);
- if (decimalData.Precision == 0) {
+ if (!decimalData.Precision.has_value()) {
ythrow TDeserializationException() << R"(missing required key "precision" for type Decimal)";
}
- if (decimalData.Scale == 0) {
+ if (!decimalData.Scale.has_value()) {
ythrow TDeserializationException() << R"(missing required key "scale" for type Decimal)";
}
- return Factory_->DecimalRaw(decimalData.Precision, decimalData.Scale);
+ return Factory_->DecimalRaw(decimalData.Precision.value(), decimalData.Scale.value());
}
case ETypeName::Json:
type = TJsonType::InstanceRaw();
@@ -420,8 +431,8 @@ namespace NTi::NIo {
auto result = event.AsScalar().AsInt64();
- if (result <= 0) {
- ythrow TDeserializationException() << what << " must be greater than zero";
+ if (result < 0) {
+ ythrow TDeserializationException() << what << " must be greater or equal to zero";
}
if (result > Max<ui8>()) {
diff --git a/library/cpp/type_info/ut/type_deserialize.cpp b/library/cpp/type_info/ut/type_deserialize.cpp
index 9e93a26bee3..7d5f60bad04 100644
--- a/library/cpp/type_info/ut/type_deserialize.cpp
+++ b/library/cpp/type_info/ut/type_deserialize.cpp
@@ -120,6 +120,40 @@ TEST(TypeDeserialize, Decimal) {
ASSERT_DESERIALIZED_EQ(NTi::Decimal(20, 10), R"({type_name=decimal; precision=20; scale=10})");
ASSERT_DESERIALIZED_EQ(NTi::Decimal(20, 10), R"({scale=10; type_name=decimal; precision=20})");
ASSERT_DESERIALIZED_EQ(NTi::Decimal(10, 10), R"({type_name=decimal; precision=10; scale=10})");
+ ASSERT_DESERIALIZED_EQ(NTi::Decimal(10, 0), R"({type_name=decimal; precision=10; scale=0})");
+}
+
+TEST(TypeDeserialize, DecimalBadTypeParameters) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=decimal; precision=0; scale=10})");
+ }(),
+ NTi::TDeserializationException, R"(invalid zero "precision")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=decimal; precision=-2; scale=10})");
+ }(),
+ NTi::TDeserializationException, R"("precision" must be greater or equal to zero)");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=decimal; precision=2; scale=-2})");
+ }(),
+ NTi::TDeserializationException, R"("scale" must be greater or equal to zero)");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=decimal; precision=2; scale=-2})");
+ }(),
+ NTi::TDeserializationException, R"("scale" must be greater or equal to zero)");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=decimal; precision=1000; scale=2})");
+ }(),
+ NTi::TDeserializationException, R"("precision" is too big)");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=decimal; precision=2; scale=1000})");
+ }(),
+ NTi::TDeserializationException, R"("scale" is too big)");
}
TEST(TypeDeserialize, DecimalMissingTypeParameters) {