Skip to content

Commit 8682ef4

Browse files
committed
add validation for different precisions
1 parent 020b684 commit 8682ef4

File tree

4 files changed

+115
-1
lines changed

4 files changed

+115
-1
lines changed

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,58 @@ 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) NOT NULL,
91+
PRIMARY KEY (Key)
92+
);
93+
)", NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync();
94+
UNIT_ASSERT_C(ddlResult.IsSuccess(), ddlResult.GetIssues().ToString());
95+
}
96+
97+
// 10000000000000 in Decimal(35, 9), invalid for Decimal(22, 9)
98+
Ydb::Value value;
99+
value.set_low_128(1864712049423024128);
100+
value.set_high_128(542);
101+
auto invalidValue = TDecimalValue(value, NYdb::TDecimalType(22, 9));
102+
103+
{
104+
auto params = TParamsBuilder()
105+
.AddParam("$value").Decimal(invalidValue).Build()
106+
.Build();
107+
108+
auto writeResult = client.ExecuteQuery(R"(
109+
UPSERT INTO DecTest (Key, Value) VALUES
110+
(1, CAST(10 AS Decimal(22,9))),
111+
(2, $value),
112+
(3, $value - CAST(1 AS Decimal(22,9)));
113+
)", NYdb::NQuery::TTxControl::BeginTx().CommitTx(), params).ExtractValueSync();
114+
115+
// TODO: Plan A, query should fail as provided value is invalid for given type.
116+
UNIT_ASSERT_C(writeResult.IsSuccess(), writeResult.GetIssues().ToString());
117+
118+
// TODO: Plan B, value for key 2 should be inf, as provided value is out of range
119+
// for given type.
120+
auto session = kikimr.GetTableClient().CreateSession().GetValueSync().GetSession();
121+
auto tableYson = ReadTableToYson(session, "/Root/DecTest");
122+
CompareYson(R"([
123+
[[1];["10"]];
124+
[[2];["inf"]];
125+
[[3];["inf"]]])", tableYson);
126+
}
127+
}
128+
77129
Y_UNIT_TEST(QueryCache) {
78130
TKikimrRunner kikimr;
79131
auto db = kikimr.GetTableClient();

ydb/library/mkql_proto/mkql_proto.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1628,7 +1628,22 @@ Y_FORCE_INLINE NUdf::TUnboxedValue KindDataImport(const TType* type, const Ydb::
16281628
return MakeString(value.bytes_value());
16291629
}
16301630
case NUdf::TDataType<NUdf::TDecimal>::Id: {
1631-
return NUdf::TUnboxedValuePod(NYql::NDecimal::FromHalfs(value.low_128(), value.high_128()));
1631+
auto data = NYql::NDecimal::FromHalfs(value.low_128(), value.high_128());
1632+
auto dataType = static_cast<const TDataType*>(type);
1633+
auto schemeType = dataType->GetSchemeType();
1634+
if (schemeType != NYql::NProto::TypeIds::Decimal) {
1635+
throw yexception() << "Expected decimal type, but found " << schemeType;
1636+
}
1637+
auto decimalType = static_cast<const TDataDecimalType *>(dataType);
1638+
auto params = decimalType->GetParams();
1639+
ui8 precision = params.first;
1640+
Y_ENSURE(precision <= NYql::NDecimal::MaxPrecision, "Unsupported decimal precision: " << precision);
1641+
if (NYql::NDecimal::IsError(data)) {
1642+
throw yexception() << "Invalid Decimal value";
1643+
}
1644+
Y_ENSURE(NYql::NDecimal::IsNormal(precision, data),
1645+
"Invalid Decimal value out of the range for specified precision: " << precision);
1646+
return NUdf::TUnboxedValuePod(data);
16321647
}
16331648
default: {
16341649
throw yexception() << "Unsupported data type: " << dataType->GetSchemeType();

yql/essentials/public/decimal/yql_decimal.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,45 @@
88
namespace NYql {
99
namespace NDecimal {
1010

11+
constexpr std::array<std::pair<TInt128, TInt128>, MaxPrecision + 1> DecimalBounds_ = {
12+
NYql::NDecimal::GetBounds<0>(),
13+
NYql::NDecimal::GetBounds<1>(),
14+
NYql::NDecimal::GetBounds<2>(),
15+
NYql::NDecimal::GetBounds<3>(),
16+
NYql::NDecimal::GetBounds<4>(),
17+
NYql::NDecimal::GetBounds<5>(),
18+
NYql::NDecimal::GetBounds<6>(),
19+
NYql::NDecimal::GetBounds<7>(),
20+
NYql::NDecimal::GetBounds<8>(),
21+
NYql::NDecimal::GetBounds<9>(),
22+
NYql::NDecimal::GetBounds<10>(),
23+
NYql::NDecimal::GetBounds<11>(),
24+
NYql::NDecimal::GetBounds<12>(),
25+
NYql::NDecimal::GetBounds<13>(),
26+
NYql::NDecimal::GetBounds<14>(),
27+
NYql::NDecimal::GetBounds<15>(),
28+
NYql::NDecimal::GetBounds<16>(),
29+
NYql::NDecimal::GetBounds<17>(),
30+
NYql::NDecimal::GetBounds<18>(),
31+
NYql::NDecimal::GetBounds<19>(),
32+
NYql::NDecimal::GetBounds<20>(),
33+
NYql::NDecimal::GetBounds<21>(),
34+
NYql::NDecimal::GetBounds<22>(),
35+
NYql::NDecimal::GetBounds<23>(),
36+
NYql::NDecimal::GetBounds<24>(),
37+
NYql::NDecimal::GetBounds<25>(),
38+
NYql::NDecimal::GetBounds<26>(),
39+
NYql::NDecimal::GetBounds<27>(),
40+
NYql::NDecimal::GetBounds<28>(),
41+
NYql::NDecimal::GetBounds<29>(),
42+
NYql::NDecimal::GetBounds<30>(),
43+
NYql::NDecimal::GetBounds<31>(),
44+
NYql::NDecimal::GetBounds<32>(),
45+
NYql::NDecimal::GetBounds<33>(),
46+
NYql::NDecimal::GetBounds<34>(),
47+
NYql::NDecimal::GetBounds<35>(),
48+
};
49+
1150
static const TUint128 Ten(10U);
1251

1352
TUint128 GetDivider(ui8 scale) {
@@ -459,5 +498,11 @@ TInt128 MulAndDivNormalDivider(TInt128 a, TInt128 b, TInt128 c) {
459498
return Normalize(Div<false>(WidenMul(a, b), TInt256(c)));
460499
}
461500

501+
bool IsNormal(ui8 precision, TInt128 v) {
502+
YQL_ENSURE(precision < DecimalBounds_.size(), "Unsupported decimal precision: %d", precision);
503+
const auto& db = DecimalBounds_[precision];
504+
return v > db.first && v < db.second;
505+
}
506+
462507
}
463508
}

yql/essentials/public/decimal/yql_decimal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ inline bool IsNormal(TInt128 v) {
7373
return v > b.first && v < b.second;
7474
}
7575

76+
inline bool IsNormal(ui8 precision, TInt128 v);
77+
7678
const char* ToString(TInt128 v, ui8 precision, ui8 scale = 0);
7779
TInt128 FromString(const TStringBuf& str, ui8 precision, ui8 scale = 0);
7880

0 commit comments

Comments
 (0)