Skip to content

Commit f3fb403

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

File tree

4 files changed

+117
-1
lines changed

4 files changed

+117
-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: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,50 @@
44
#include <ostream>
55
#include <string>
66
#include <charconv>
7+
#include <array>
78

89
namespace NYql {
910
namespace NDecimal {
1011

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

1353
TUint128 GetDivider(ui8 scale) {
@@ -459,5 +499,12 @@ TInt128 MulAndDivNormalDivider(TInt128 a, TInt128 b, TInt128 c) {
459499
return Normalize(Div<false>(WidenMul(a, b), TInt256(c)));
460500
}
461501

502+
bool IsNormal(ui8 precision, TInt128 v) {
503+
if (precision >= DecimalBounds.size())
504+
return false;
505+
const auto& db = DecimalBounds[precision];
506+
return v > db.first && v < db.second;
507+
}
508+
462509
}
463510
}

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)