Skip to content

Commit 91d7f36

Browse files
committed
add validation for different precisions
1 parent 020b684 commit 91d7f36

File tree

2 files changed

+136
-1
lines changed

2 files changed

+136
-1
lines changed

ydb/core/kqp/ut/query/kqp_query_ut.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,84 @@ Y_UNIT_TEST_SUITE(KqpQuery) {
7474
UNIT_ASSERT_VALUES_EQUAL(counters.RecompileRequestGet()->Val(), 1);
7575
}
7676

77+
Y_UNIT_TEST_QUAD(DecimalOutOfPrecision, UseOltpSink, EnableParameterizedDecimal) {
78+
TKikimrSettings serverSettings;
79+
serverSettings.AppConfig.MutableTableServiceConfig()->SetEnableOltpSink(UseOltpSink);
80+
serverSettings.FeatureFlags.SetEnableParameterizedDecimal(EnableParameterizedDecimal);
81+
serverSettings.WithSampleTables = false;
82+
83+
TKikimrRunner kikimr(serverSettings);
84+
auto client = kikimr.GetQueryClient();
85+
86+
{
87+
auto ddlResult = client.ExecuteQuery(R"(
88+
CREATE TABLE DecTest (
89+
Key Int32 NOT NULL,
90+
Value Decimal(22, 9),
91+
ValueLarge Decimal(35, 9),
92+
PRIMARY KEY (Key)
93+
);
94+
)", NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync();
95+
UNIT_ASSERT_C(ddlResult.IsSuccess(), ddlResult.GetIssues().ToString());
96+
}
97+
98+
// 10000000000000 in Decimal(35, 9), invalid for Decimal(22, 9)
99+
Ydb::Value value;
100+
value.set_low_128(1864712049423024128);
101+
value.set_high_128(542);
102+
auto invalidValue = TDecimalValue(value, NYdb::TDecimalType(22, 9));
103+
104+
auto validValue = TDecimalValue(value, NYdb::TDecimalType(35, 9));
105+
106+
{
107+
auto params = TParamsBuilder()
108+
.AddParam("$value").Decimal(invalidValue).Build()
109+
.Build();
110+
111+
auto writeResult = client.ExecuteQuery(R"(
112+
UPSERT INTO DecTest (Key, Value) VALUES
113+
(1, CAST(10 AS Decimal(22,9))),
114+
(2, $value),
115+
(3, $value - CAST(1 AS Decimal(22,9)));
116+
)", NYdb::NQuery::TTxControl::BeginTx().CommitTx(), params).ExtractValueSync();
117+
118+
// TODO: Plan A, query should fail as provided value is invalid for given type.
119+
UNIT_ASSERT_C(!writeResult.IsSuccess(), writeResult.GetIssues().ToString());
120+
UNIT_ASSERT_EQUAL_C(writeResult.GetStatus(), EStatus::BAD_REQUEST, writeResult.GetIssues().ToString());
121+
122+
// TODO: Plan B, value for key 2 should be inf, as provided value is out of range
123+
// for given type.
124+
auto session = kikimr.GetTableClient().CreateSession().GetValueSync().GetSession();
125+
auto tableYson = ReadTableToYson(session, "/Root/DecTest");
126+
CompareYson(R"([])", tableYson);
127+
{
128+
auto paramsValid = TParamsBuilder()
129+
.AddParam("$value").Decimal(validValue).Build()
130+
.Build();
131+
auto writeResult = client.ExecuteQuery(R"(
132+
UPSERT INTO DecTest (Key, ValueLarge) VALUES
133+
(2, $value);
134+
)", NYdb::NQuery::TTxControl::BeginTx().CommitTx(), paramsValid).ExtractValueSync();
135+
136+
// TODO: Plan A, query should fail as provided value is invalid for given type.
137+
UNIT_ASSERT_C(writeResult.IsSuccess(), writeResult.GetIssues().ToString());
138+
UNIT_ASSERT_EQUAL_C(writeResult.GetStatus(), EStatus::SUCCESS, writeResult.GetIssues().ToString());
139+
140+
auto tableYson = ReadTableToYson(session, "/Root/DecTest");
141+
CompareYson(R"([[[2];#;["10000000000000"]]])", tableYson);
142+
}
143+
144+
{
145+
auto writeResult = client.ExecuteQuery(R"(
146+
UPSERT INTO DecTest (Key, Value) SELECT Key, ValueLarge as Value FROM DecTest;
147+
)", NYdb::NQuery::TTxControl::BeginTx().CommitTx()).ExtractValueSync();
148+
// TODO: Plan A, query should fail as provided value is invalid for given type.
149+
UNIT_ASSERT_C(!writeResult.IsSuccess(), writeResult.GetIssues().ToString());
150+
UNIT_ASSERT_EQUAL_C(writeResult.GetStatus(), EStatus::GENERIC_ERROR, writeResult.GetIssues().ToString());
151+
}
152+
}
153+
}
154+
77155
Y_UNIT_TEST(QueryCache) {
78156
TKikimrRunner kikimr;
79157
auto db = kikimr.GetTableClient();

ydb/library/mkql_proto/mkql_proto.cpp

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,52 @@ namespace NKikimr::NMiniKQL {
2020

2121
namespace {
2222

23+
static constexpr std::array<std::pair<NYql::NDecimal::TInt128, NYql::NDecimal::TInt128>, NYql::NDecimal::MaxPrecision + 1> DecimalBounds = {
24+
NYql::NDecimal::GetBounds<0>(),
25+
NYql::NDecimal::GetBounds<1>(),
26+
NYql::NDecimal::GetBounds<2>(),
27+
NYql::NDecimal::GetBounds<3>(),
28+
NYql::NDecimal::GetBounds<4>(),
29+
NYql::NDecimal::GetBounds<5>(),
30+
NYql::NDecimal::GetBounds<6>(),
31+
NYql::NDecimal::GetBounds<7>(),
32+
NYql::NDecimal::GetBounds<8>(),
33+
NYql::NDecimal::GetBounds<9>(),
34+
NYql::NDecimal::GetBounds<10>(),
35+
NYql::NDecimal::GetBounds<11>(),
36+
NYql::NDecimal::GetBounds<12>(),
37+
NYql::NDecimal::GetBounds<13>(),
38+
NYql::NDecimal::GetBounds<14>(),
39+
NYql::NDecimal::GetBounds<15>(),
40+
NYql::NDecimal::GetBounds<16>(),
41+
NYql::NDecimal::GetBounds<17>(),
42+
NYql::NDecimal::GetBounds<18>(),
43+
NYql::NDecimal::GetBounds<19>(),
44+
NYql::NDecimal::GetBounds<20>(),
45+
NYql::NDecimal::GetBounds<21>(),
46+
NYql::NDecimal::GetBounds<22>(),
47+
NYql::NDecimal::GetBounds<23>(),
48+
NYql::NDecimal::GetBounds<24>(),
49+
NYql::NDecimal::GetBounds<25>(),
50+
NYql::NDecimal::GetBounds<26>(),
51+
NYql::NDecimal::GetBounds<27>(),
52+
NYql::NDecimal::GetBounds<28>(),
53+
NYql::NDecimal::GetBounds<29>(),
54+
NYql::NDecimal::GetBounds<30>(),
55+
NYql::NDecimal::GetBounds<31>(),
56+
NYql::NDecimal::GetBounds<32>(),
57+
NYql::NDecimal::GetBounds<33>(),
58+
NYql::NDecimal::GetBounds<34>(),
59+
NYql::NDecimal::GetBounds<35>(),
60+
};
61+
62+
bool IsValidDecimal(ui8 precision, NYql::NDecimal::TInt128 v) {
63+
if (precision >= DecimalBounds.size())
64+
return false;
65+
const auto& db = DecimalBounds[precision];
66+
return v > db.first && v < db.second;
67+
}
68+
2369
void ExportTypeToProtoImpl(TType* type, NKikimrMiniKQL::TType& res, const TVector<ui32>* columnOrder = nullptr);
2470

2571
Y_FORCE_INLINE void HandleKindDataExport(const TType* type, const NUdf::TUnboxedValuePod& value, Ydb::Value& res) {
@@ -1628,7 +1674,18 @@ Y_FORCE_INLINE NUdf::TUnboxedValue KindDataImport(const TType* type, const Ydb::
16281674
return MakeString(value.bytes_value());
16291675
}
16301676
case NUdf::TDataType<NUdf::TDecimal>::Id: {
1631-
return NUdf::TUnboxedValuePod(NYql::NDecimal::FromHalfs(value.low_128(), value.high_128()));
1677+
auto data = NYql::NDecimal::FromHalfs(value.low_128(), value.high_128());
1678+
auto dataType = static_cast<const TDataType*>(type);
1679+
auto schemeType = dataType->GetSchemeType();
1680+
Y_ENSURE(schemeType == NYql::NProto::TypeIds::Decimal, "Expected decimal type, but found " << schemeType);
1681+
auto decimalType = static_cast<const TDataDecimalType *>(dataType);
1682+
auto params = decimalType->GetParams();
1683+
ui8 precision = params.first;
1684+
Y_ENSURE(precision <= NYql::NDecimal::MaxPrecision, "Unsupported decimal precision: " << precision);
1685+
Y_ENSURE(!NYql::NDecimal::IsError(data), "Invalid Decimal value");
1686+
Y_ENSURE(IsValidDecimal(precision, data) || NYql::NDecimal::IsNan(data) || NYql::NDecimal::IsInf(data),
1687+
"Invalid Decimal value out of the range for specified precision: " << precision);
1688+
return NUdf::TUnboxedValuePod(data);
16321689
}
16331690
default: {
16341691
throw yexception() << "Unsupported data type: " << dataType->GetSchemeType();

0 commit comments

Comments
 (0)