Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions ydb/core/io_formats/cell_maker/cell_maker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -518,13 +518,13 @@ bool CheckCellValue(const TCell& cell, const NScheme::TTypeInfo& typeInfo) {
case NScheme::NTypeIds::Interval:
return (ui64)std::abs(cell.AsValue<i64>()) < NUdf::MAX_TIMESTAMP;
case NScheme::NTypeIds::Date32:
return cell.AsValue<i32>() < NUdf::MAX_DATE32;
return cell.AsValue<i32>() >= NUdf::MIN_DATE32 && cell.AsValue<i32>() <= NUdf::MAX_DATE32;
case NScheme::NTypeIds::Datetime64:
return cell.AsValue<i64>() < NUdf::MAX_DATETIME64;
return cell.AsValue<i64>() >= NUdf::MIN_DATETIME64 && cell.AsValue<i64>() <= NUdf::MAX_DATETIME64;
case NScheme::NTypeIds::Timestamp64:
return cell.AsValue<i64>() < NUdf::MAX_TIMESTAMP64;
return cell.AsValue<i64>() >= NUdf::MIN_TIMESTAMP64 && cell.AsValue<i64>() <= NUdf::MAX_TIMESTAMP64;
case NScheme::NTypeIds::Interval64:
return std::abs(cell.AsValue<i64>()) < NUdf::MAX_INTERVAL64;
return std::abs(cell.AsValue<i64>()) <= NUdf::MAX_INTERVAL64;
case NScheme::NTypeIds::Utf8:
return NYql::IsUtf8(cell.AsBuf());
case NScheme::NTypeIds::Yson:
Expand Down
110 changes: 110 additions & 0 deletions ydb/core/kqp/ut/query/kqp_query_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <yql/essentials/ast/yql_ast.h>
#include <yql/essentials/ast/yql_expr.h>
#include <yql/essentials/core/yql_expr_optimize.h>
#include <yql/essentials/public/udf/udf_data_type.h>

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

Expand Down Expand Up @@ -74,6 +75,115 @@ Y_UNIT_TEST_SUITE(KqpQuery) {
UNIT_ASSERT_VALUES_EQUAL(counters.RecompileRequestGet()->Val(), 1);
}

Y_UNIT_TEST_TWIN(ExtendedTimeOutOfBounds, BulkUpsert) {
auto settings = TKikimrSettings().SetWithSampleTables(false);
TKikimrRunner kikimr(settings);

auto queryClient = kikimr.GetQueryClient();
auto tableClient = kikimr.GetTableClient();

{
const std::string query = R"(
CREATE TABLE `/Root/TimeTable` (
Key UInt32 NOT NULL,
V_Date32 Date32,
V_Datetime64 Datetime64,
V_Timestamp64 Timestamp64,
V_Interval64 Interval64,
PRIMARY KEY (Key)
);
)";
auto result = queryClient.ExecuteQuery(query, NQuery::TTxControl::NoTx()).ExtractValueSync();
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString());
}

auto fUpsertAndCheck = [&]<typename T>(ui32 key, T value, bool success) {
std::string colName;
if (BulkUpsert) {
TValueBuilder rows;
rows.BeginList();
rows.AddListItem().BeginStruct().AddMember("Key").Uint32(key);
if constexpr (std::is_same_v<T, TWideDays>) {
rows.AddMember("V_Date32").Date32(std::chrono::sys_time<TWideDays>(TWideDays(value)));
colName = "V_Date32";
} else if constexpr (std::is_same_v<T, TWideSeconds>) {
rows.AddMember("V_Datetime64").Datetime64(std::chrono::sys_time<TWideSeconds>(TWideSeconds(value)));
colName = "V_Datetime64";
} else if constexpr (std::is_same_v<T, TWideMicroseconds>) {
rows.AddMember("V_Timestamp64").Timestamp64(std::chrono::sys_time<TWideMicroseconds>(TWideMicroseconds(value)));
colName = "V_Timestamp64";
} else if constexpr (std::is_same_v<T, i64>) {
rows.AddMember("V_Interval64").Interval64(TWideMicroseconds(value));
colName = "V_Interval64";
} else {
UNIT_ASSERT_C(false, "Unsupported type");
}
rows.EndStruct().EndList();

auto result = tableClient.BulkUpsert("/Root/TimeTable", rows.Build()).GetValueSync();
UNIT_ASSERT_VALUES_EQUAL_C(result.IsSuccess(), success, result.GetIssues().ToString());
} else {
auto params = std::move(TParamsBuilder().AddParam("$key").Uint32(key).Build());
if constexpr (std::is_same_v<T, TWideDays>) {
params.AddParam("$param").Date32(std::chrono::sys_time<TWideDays>(TWideDays(value))).Build();
colName = "V_Date32";
} else if constexpr (std::is_same_v<T, TWideSeconds>) {
params.AddParam("$param").Datetime64(std::chrono::sys_time<TWideSeconds>(TWideSeconds(value))).Build();
colName = "V_Datetime64";
} else if constexpr (std::is_same_v<T, TWideMicroseconds>) {
params.AddParam("$param").Timestamp64(std::chrono::sys_time<TWideMicroseconds>(TWideMicroseconds(value))).Build();
colName = "V_Timestamp64";
} else if constexpr (std::is_same_v<T, i64>) {
params.AddParam("$param").Interval64(TWideMicroseconds(value)).Build();
colName = "V_Interval64";
} else {
UNIT_ASSERT_C(false, "Unsupported type");
}

auto result = queryClient.ExecuteQuery(Sprintf(R"(
UPSERT INTO `/Root/TimeTable` (Key, %s) VALUES ($key, $param);
)", colName.c_str()), NQuery::TTxControl::NoTx(), params.Build()).ExtractValueSync();
UNIT_ASSERT_VALUES_EQUAL_C(result.IsSuccess(), success, result.GetIssues().ToString());
}
};

{
// Date32
fUpsertAndCheck(1, TWideDays(0), /* success */ true); // Basic
fUpsertAndCheck(2, TWideDays(NYql::NUdf::MIN_DATE32), /* success */ true); // Min is inclusive
fUpsertAndCheck(3, TWideDays(NYql::NUdf::MAX_DATE32), /* success */ true); // Max is inclusive
fUpsertAndCheck(4, TWideDays(NYql::NUdf::MIN_DATE32 - 1), /* success */ false); // Out of bounds
fUpsertAndCheck(5, TWideDays(NYql::NUdf::MAX_DATE32 + 1), /* success */ false); // Out of bounds
}

{
// Datetime64
fUpsertAndCheck(11, TWideSeconds(0), /* success */ true); // Basic
fUpsertAndCheck(12, TWideSeconds(NYql::NUdf::MIN_DATETIME64), /* success */ true); // Min is inclusive
fUpsertAndCheck(13, TWideSeconds(NYql::NUdf::MAX_DATETIME64), /* success */ true); // Max is inclusive
fUpsertAndCheck(14, TWideSeconds(NYql::NUdf::MIN_DATETIME64 - 1), /* success */ false); // Out of bounds
fUpsertAndCheck(15, TWideSeconds(NYql::NUdf::MAX_DATETIME64 + 1), /* success */ false); // Out of bounds
}

{
// Timestamp64
fUpsertAndCheck(21, TWideMicroseconds(0), /* success */ true); // Basic
fUpsertAndCheck(22, TWideMicroseconds(NYql::NUdf::MIN_TIMESTAMP64), /* success */ true); // Min is inclusive
fUpsertAndCheck(23, TWideMicroseconds(NYql::NUdf::MAX_TIMESTAMP64), /* success */ true); // Max is inclusive
fUpsertAndCheck(24, TWideMicroseconds(NYql::NUdf::MIN_TIMESTAMP64 - 1), /* success */ false); // Out of bounds
fUpsertAndCheck(25, TWideMicroseconds(NYql::NUdf::MAX_TIMESTAMP64 + 1), /* success */ false); // Out of bounds
}

{
// Interval64
fUpsertAndCheck(31, static_cast<i64>(0), /* success */ true); // Basic
fUpsertAndCheck(32, NYql::NUdf::MAX_INTERVAL64, /* success */ true); // Max is inclusive
fUpsertAndCheck(33, -NYql::NUdf::MAX_INTERVAL64, /* success */ true); // -Max is inclusive
fUpsertAndCheck(34, NYql::NUdf::MAX_INTERVAL64 + 1, /* success */ false); // Out of bounds
fUpsertAndCheck(35, -(NYql::NUdf::MAX_INTERVAL64 + 1), /* success */ false); // Out of bounds
}
}

Y_UNIT_TEST_TWIN(DecimalOutOfPrecisionBulk, EnableParameterizedDecimal) {
TKikimrSettings serverSettings;
serverSettings.FeatureFlags.SetEnableParameterizedDecimal(EnableParameterizedDecimal);
Expand Down
16 changes: 8 additions & 8 deletions ydb/core/ydb_convert/ydb_convert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,28 +480,28 @@ Y_FORCE_INLINE void ConvertData(NUdf::TDataTypeId typeId, const Ydb::Value& valu
break;
case NUdf::TDataType<NUdf::TDate32>::Id:
CheckTypeId(value.value_case(), Ydb::Value::kInt32Value, "Date32");
if (value.int32_value() >= NUdf::MAX_DATE32) {
if (value.int32_value() < NUdf::MIN_DATE32 || value.int32_value() > NUdf::MAX_DATE32) {
throw yexception() << "Invalid Date32 value";
}
res.SetInt32(value.int32_value());
break;
case NUdf::TDataType<NUdf::TDatetime64>::Id:
CheckTypeId(value.value_case(), Ydb::Value::kInt64Value, "Datetime64");
if (value.int64_value() >= NUdf::MAX_DATETIME64) {
if (value.int64_value() < NUdf::MIN_DATETIME64 || value.int64_value() > NUdf::MAX_DATETIME64) {
throw yexception() << "Invalid Datetime64 value";
}
res.SetInt64(value.int64_value());
break;
case NUdf::TDataType<NUdf::TTimestamp64>::Id:
CheckTypeId(value.value_case(), Ydb::Value::kInt64Value, "Timestamp64");
if (value.int64_value() >= NUdf::MAX_TIMESTAMP64) {
if (value.int64_value() < NUdf::MIN_TIMESTAMP64 || value.int64_value() > NUdf::MAX_TIMESTAMP64) {
throw yexception() << "Invalid Timestamp64 value";
}
res.SetInt64(value.int64_value());
break;
case NUdf::TDataType<NUdf::TInterval64>::Id:
CheckTypeId(value.value_case(), Ydb::Value::kInt64Value, "Interval64");
if (std::abs(value.int64_value()) >= NUdf::MAX_INTERVAL64) {
if (std::abs(value.int64_value()) > NUdf::MAX_INTERVAL64) {
throw yexception() << "Invalid Interval64 value";
}
res.SetInt64(value.int64_value());
Expand Down Expand Up @@ -1123,19 +1123,19 @@ bool CheckValueData(NScheme::TTypeInfo type, const TCell& cell, TString& err) {
break;

case NScheme::NTypeIds::Date32:
ok = cell.AsValue<i32>() < NUdf::MAX_DATE32;
ok = cell.AsValue<i32>() >= NUdf::MIN_DATE32 && cell.AsValue<i32>() <= NUdf::MAX_DATE32;
break;

case NScheme::NTypeIds::Datetime64:
ok = cell.AsValue<i64>() < NUdf::MAX_DATETIME64;
ok = cell.AsValue<i64>() >= NUdf::MIN_DATETIME64 && cell.AsValue<i64>() <= NUdf::MAX_DATETIME64;
break;

case NScheme::NTypeIds::Timestamp64:
ok = cell.AsValue<i64>() < NUdf::MAX_TIMESTAMP64;
ok = cell.AsValue<i64>() >= NUdf::MIN_TIMESTAMP64 && cell.AsValue<i64>() <= NUdf::MAX_TIMESTAMP64;
break;

case NScheme::NTypeIds::Interval64:
ok = std::abs(cell.AsValue<i64>()) < NUdf::MAX_INTERVAL64;
ok = std::abs(cell.AsValue<i64>()) <= NUdf::MAX_INTERVAL64;
break;

case NScheme::NTypeIds::Utf8:
Expand Down
Loading