Skip to content

Commit e452dca

Browse files
authored
Fix check bounds for time extended types (#28313)
1 parent 47ec58a commit e452dca

File tree

3 files changed

+122
-12
lines changed

3 files changed

+122
-12
lines changed

ydb/core/io_formats/cell_maker/cell_maker.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -518,13 +518,13 @@ bool CheckCellValue(const TCell& cell, const NScheme::TTypeInfo& typeInfo) {
518518
case NScheme::NTypeIds::Interval:
519519
return (ui64)std::abs(cell.AsValue<i64>()) < NUdf::MAX_TIMESTAMP;
520520
case NScheme::NTypeIds::Date32:
521-
return cell.AsValue<i32>() < NUdf::MAX_DATE32;
521+
return cell.AsValue<i32>() >= NUdf::MIN_DATE32 && cell.AsValue<i32>() <= NUdf::MAX_DATE32;
522522
case NScheme::NTypeIds::Datetime64:
523-
return cell.AsValue<i64>() < NUdf::MAX_DATETIME64;
523+
return cell.AsValue<i64>() >= NUdf::MIN_DATETIME64 && cell.AsValue<i64>() <= NUdf::MAX_DATETIME64;
524524
case NScheme::NTypeIds::Timestamp64:
525-
return cell.AsValue<i64>() < NUdf::MAX_TIMESTAMP64;
525+
return cell.AsValue<i64>() >= NUdf::MIN_TIMESTAMP64 && cell.AsValue<i64>() <= NUdf::MAX_TIMESTAMP64;
526526
case NScheme::NTypeIds::Interval64:
527-
return std::abs(cell.AsValue<i64>()) < NUdf::MAX_INTERVAL64;
527+
return std::abs(cell.AsValue<i64>()) <= NUdf::MAX_INTERVAL64;
528528
case NScheme::NTypeIds::Utf8:
529529
return NYql::IsUtf8(cell.AsBuf());
530530
case NScheme::NTypeIds::Yson:

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

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <yql/essentials/ast/yql_ast.h>
1212
#include <yql/essentials/ast/yql_expr.h>
1313
#include <yql/essentials/core/yql_expr_optimize.h>
14+
#include <yql/essentials/public/udf/udf_data_type.h>
1415

1516
#include <library/cpp/json/json_reader.h>
1617

@@ -74,6 +75,115 @@ Y_UNIT_TEST_SUITE(KqpQuery) {
7475
UNIT_ASSERT_VALUES_EQUAL(counters.RecompileRequestGet()->Val(), 1);
7576
}
7677

78+
Y_UNIT_TEST_TWIN(ExtendedTimeOutOfBounds, BulkUpsert) {
79+
auto settings = TKikimrSettings().SetWithSampleTables(false);
80+
TKikimrRunner kikimr(settings);
81+
82+
auto queryClient = kikimr.GetQueryClient();
83+
auto tableClient = kikimr.GetTableClient();
84+
85+
{
86+
const std::string query = R"(
87+
CREATE TABLE `/Root/TimeTable` (
88+
Key UInt32 NOT NULL,
89+
V_Date32 Date32,
90+
V_Datetime64 Datetime64,
91+
V_Timestamp64 Timestamp64,
92+
V_Interval64 Interval64,
93+
PRIMARY KEY (Key)
94+
);
95+
)";
96+
auto result = queryClient.ExecuteQuery(query, NQuery::TTxControl::NoTx()).ExtractValueSync();
97+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString());
98+
}
99+
100+
auto fUpsertAndCheck = [&]<typename T>(ui32 key, T value, bool success) {
101+
std::string colName;
102+
if (BulkUpsert) {
103+
TValueBuilder rows;
104+
rows.BeginList();
105+
rows.AddListItem().BeginStruct().AddMember("Key").Uint32(key);
106+
if constexpr (std::is_same_v<T, TWideDays>) {
107+
rows.AddMember("V_Date32").Date32(std::chrono::sys_time<TWideDays>(TWideDays(value)));
108+
colName = "V_Date32";
109+
} else if constexpr (std::is_same_v<T, TWideSeconds>) {
110+
rows.AddMember("V_Datetime64").Datetime64(std::chrono::sys_time<TWideSeconds>(TWideSeconds(value)));
111+
colName = "V_Datetime64";
112+
} else if constexpr (std::is_same_v<T, TWideMicroseconds>) {
113+
rows.AddMember("V_Timestamp64").Timestamp64(std::chrono::sys_time<TWideMicroseconds>(TWideMicroseconds(value)));
114+
colName = "V_Timestamp64";
115+
} else if constexpr (std::is_same_v<T, i64>) {
116+
rows.AddMember("V_Interval64").Interval64(TWideMicroseconds(value));
117+
colName = "V_Interval64";
118+
} else {
119+
UNIT_ASSERT_C(false, "Unsupported type");
120+
}
121+
rows.EndStruct().EndList();
122+
123+
auto result = tableClient.BulkUpsert("/Root/TimeTable", rows.Build()).GetValueSync();
124+
UNIT_ASSERT_VALUES_EQUAL_C(result.IsSuccess(), success, result.GetIssues().ToString());
125+
} else {
126+
auto params = std::move(TParamsBuilder().AddParam("$key").Uint32(key).Build());
127+
if constexpr (std::is_same_v<T, TWideDays>) {
128+
params.AddParam("$param").Date32(std::chrono::sys_time<TWideDays>(TWideDays(value))).Build();
129+
colName = "V_Date32";
130+
} else if constexpr (std::is_same_v<T, TWideSeconds>) {
131+
params.AddParam("$param").Datetime64(std::chrono::sys_time<TWideSeconds>(TWideSeconds(value))).Build();
132+
colName = "V_Datetime64";
133+
} else if constexpr (std::is_same_v<T, TWideMicroseconds>) {
134+
params.AddParam("$param").Timestamp64(std::chrono::sys_time<TWideMicroseconds>(TWideMicroseconds(value))).Build();
135+
colName = "V_Timestamp64";
136+
} else if constexpr (std::is_same_v<T, i64>) {
137+
params.AddParam("$param").Interval64(TWideMicroseconds(value)).Build();
138+
colName = "V_Interval64";
139+
} else {
140+
UNIT_ASSERT_C(false, "Unsupported type");
141+
}
142+
143+
auto result = queryClient.ExecuteQuery(Sprintf(R"(
144+
UPSERT INTO `/Root/TimeTable` (Key, %s) VALUES ($key, $param);
145+
)", colName.c_str()), NQuery::TTxControl::NoTx(), params.Build()).ExtractValueSync();
146+
UNIT_ASSERT_VALUES_EQUAL_C(result.IsSuccess(), success, result.GetIssues().ToString());
147+
}
148+
};
149+
150+
{
151+
// Date32
152+
fUpsertAndCheck(1, TWideDays(0), /* success */ true); // Basic
153+
fUpsertAndCheck(2, TWideDays(NYql::NUdf::MIN_DATE32), /* success */ true); // Min is inclusive
154+
fUpsertAndCheck(3, TWideDays(NYql::NUdf::MAX_DATE32), /* success */ true); // Max is inclusive
155+
fUpsertAndCheck(4, TWideDays(NYql::NUdf::MIN_DATE32 - 1), /* success */ false); // Out of bounds
156+
fUpsertAndCheck(5, TWideDays(NYql::NUdf::MAX_DATE32 + 1), /* success */ false); // Out of bounds
157+
}
158+
159+
{
160+
// Datetime64
161+
fUpsertAndCheck(11, TWideSeconds(0), /* success */ true); // Basic
162+
fUpsertAndCheck(12, TWideSeconds(NYql::NUdf::MIN_DATETIME64), /* success */ true); // Min is inclusive
163+
fUpsertAndCheck(13, TWideSeconds(NYql::NUdf::MAX_DATETIME64), /* success */ true); // Max is inclusive
164+
fUpsertAndCheck(14, TWideSeconds(NYql::NUdf::MIN_DATETIME64 - 1), /* success */ false); // Out of bounds
165+
fUpsertAndCheck(15, TWideSeconds(NYql::NUdf::MAX_DATETIME64 + 1), /* success */ false); // Out of bounds
166+
}
167+
168+
{
169+
// Timestamp64
170+
fUpsertAndCheck(21, TWideMicroseconds(0), /* success */ true); // Basic
171+
fUpsertAndCheck(22, TWideMicroseconds(NYql::NUdf::MIN_TIMESTAMP64), /* success */ true); // Min is inclusive
172+
fUpsertAndCheck(23, TWideMicroseconds(NYql::NUdf::MAX_TIMESTAMP64), /* success */ true); // Max is inclusive
173+
fUpsertAndCheck(24, TWideMicroseconds(NYql::NUdf::MIN_TIMESTAMP64 - 1), /* success */ false); // Out of bounds
174+
fUpsertAndCheck(25, TWideMicroseconds(NYql::NUdf::MAX_TIMESTAMP64 + 1), /* success */ false); // Out of bounds
175+
}
176+
177+
{
178+
// Interval64
179+
fUpsertAndCheck(31, static_cast<i64>(0), /* success */ true); // Basic
180+
fUpsertAndCheck(32, NYql::NUdf::MAX_INTERVAL64, /* success */ true); // Max is inclusive
181+
fUpsertAndCheck(33, -NYql::NUdf::MAX_INTERVAL64, /* success */ true); // -Max is inclusive
182+
fUpsertAndCheck(34, NYql::NUdf::MAX_INTERVAL64 + 1, /* success */ false); // Out of bounds
183+
fUpsertAndCheck(35, -(NYql::NUdf::MAX_INTERVAL64 + 1), /* success */ false); // Out of bounds
184+
}
185+
}
186+
77187
Y_UNIT_TEST_TWIN(DecimalOutOfPrecisionBulk, EnableParameterizedDecimal) {
78188
TKikimrSettings serverSettings;
79189
serverSettings.FeatureFlags.SetEnableParameterizedDecimal(EnableParameterizedDecimal);

ydb/core/ydb_convert/ydb_convert.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -480,28 +480,28 @@ Y_FORCE_INLINE void ConvertData(NUdf::TDataTypeId typeId, const Ydb::Value& valu
480480
break;
481481
case NUdf::TDataType<NUdf::TDate32>::Id:
482482
CheckTypeId(value.value_case(), Ydb::Value::kInt32Value, "Date32");
483-
if (value.int32_value() >= NUdf::MAX_DATE32) {
483+
if (value.int32_value() < NUdf::MIN_DATE32 || value.int32_value() > NUdf::MAX_DATE32) {
484484
throw yexception() << "Invalid Date32 value";
485485
}
486486
res.SetInt32(value.int32_value());
487487
break;
488488
case NUdf::TDataType<NUdf::TDatetime64>::Id:
489489
CheckTypeId(value.value_case(), Ydb::Value::kInt64Value, "Datetime64");
490-
if (value.int64_value() >= NUdf::MAX_DATETIME64) {
490+
if (value.int64_value() < NUdf::MIN_DATETIME64 || value.int64_value() > NUdf::MAX_DATETIME64) {
491491
throw yexception() << "Invalid Datetime64 value";
492492
}
493493
res.SetInt64(value.int64_value());
494494
break;
495495
case NUdf::TDataType<NUdf::TTimestamp64>::Id:
496496
CheckTypeId(value.value_case(), Ydb::Value::kInt64Value, "Timestamp64");
497-
if (value.int64_value() >= NUdf::MAX_TIMESTAMP64) {
497+
if (value.int64_value() < NUdf::MIN_TIMESTAMP64 || value.int64_value() > NUdf::MAX_TIMESTAMP64) {
498498
throw yexception() << "Invalid Timestamp64 value";
499499
}
500500
res.SetInt64(value.int64_value());
501501
break;
502502
case NUdf::TDataType<NUdf::TInterval64>::Id:
503503
CheckTypeId(value.value_case(), Ydb::Value::kInt64Value, "Interval64");
504-
if (std::abs(value.int64_value()) >= NUdf::MAX_INTERVAL64) {
504+
if (std::abs(value.int64_value()) > NUdf::MAX_INTERVAL64) {
505505
throw yexception() << "Invalid Interval64 value";
506506
}
507507
res.SetInt64(value.int64_value());
@@ -1123,19 +1123,19 @@ bool CheckValueData(NScheme::TTypeInfo type, const TCell& cell, TString& err) {
11231123
break;
11241124

11251125
case NScheme::NTypeIds::Date32:
1126-
ok = cell.AsValue<i32>() < NUdf::MAX_DATE32;
1126+
ok = cell.AsValue<i32>() >= NUdf::MIN_DATE32 && cell.AsValue<i32>() <= NUdf::MAX_DATE32;
11271127
break;
11281128

11291129
case NScheme::NTypeIds::Datetime64:
1130-
ok = cell.AsValue<i64>() < NUdf::MAX_DATETIME64;
1130+
ok = cell.AsValue<i64>() >= NUdf::MIN_DATETIME64 && cell.AsValue<i64>() <= NUdf::MAX_DATETIME64;
11311131
break;
11321132

11331133
case NScheme::NTypeIds::Timestamp64:
1134-
ok = cell.AsValue<i64>() < NUdf::MAX_TIMESTAMP64;
1134+
ok = cell.AsValue<i64>() >= NUdf::MIN_TIMESTAMP64 && cell.AsValue<i64>() <= NUdf::MAX_TIMESTAMP64;
11351135
break;
11361136

11371137
case NScheme::NTypeIds::Interval64:
1138-
ok = std::abs(cell.AsValue<i64>()) < NUdf::MAX_INTERVAL64;
1138+
ok = std::abs(cell.AsValue<i64>()) <= NUdf::MAX_INTERVAL64;
11391139
break;
11401140

11411141
case NScheme::NTypeIds::Utf8:

0 commit comments

Comments
 (0)