From 472cfacddfbb7145ceabd9eb02f25b0e100c9dcc Mon Sep 17 00:00:00 2001 From: Sergey Volynkin Date: Mon, 14 Jul 2025 11:21:19 +0300 Subject: [PATCH] Support for UUID, issue 13047 --- .../formats/arrow/arrow_batch_builder.cpp | 8 ++ ydb/core/formats/arrow/arrow_helpers.cpp | 12 +- ydb/core/formats/arrow/converter.h | 5 + ydb/core/formats/arrow/switch/switch_type.h | 3 + ydb/core/grpc_services/rpc_load_rows.cpp | 5 +- ydb/core/kqp/runtime/kqp_scan_data.cpp | 22 ++++ ydb/core/kqp/runtime/kqp_scan_data_ut.cpp | 26 +++- ydb/core/kqp/ut/arrow/kqp_types_arrow_ut.cpp | 12 +- ydb/core/kqp/ut/common/columnshard.cpp | 3 + ydb/core/kqp/ut/olap/uuid_ut.cpp | 119 ++++++++++++++++++ ydb/core/kqp/ut/olap/ya.make | 1 + ydb/core/kqp/ut/scheme/kqp_scheme_ut.cpp | 2 +- .../test_helper/columnshard_ut_common.h | 8 ++ .../transactions/operators/schema.cpp | 3 +- .../tx/schemeshard/olap/columns/update.cpp | 1 + .../formats/arrow/csv/converter/csv_arrow.cpp | 22 ++++ ydb/library/formats/arrow/switch/compare.h | 1 + .../stress/olap_workload/workload/__init__.py | 2 +- yql/essentials/minikql/arrow/arrow_util.h | 26 ++++ yql/essentials/minikql/mkql_type_builder.cpp | 4 +- yql/essentials/public/udf/arrow/block_item.h | 29 ++++- .../public/udf/arrow/block_item_hasher.h | 8 ++ .../public/udf/arrow/block_reader.h | 4 +- .../public/udf/arrow/dispatch_traits.h | 16 ++- yql/essentials/public/udf/udf_value.h | 6 +- yql/essentials/public/udf/udf_value_inl.h | 19 +++ yql/essentials/public/uuid/ya.make | 8 ++ yql/essentials/public/uuid/yql_uuid.cpp | 1 + yql/essentials/public/uuid/yql_uuid.h | 35 ++++++ yql/essentials/public/ya.make | 1 + .../providers/yt/codec/yt_arrow_converter.cpp | 5 +- 31 files changed, 389 insertions(+), 28 deletions(-) create mode 100644 ydb/core/kqp/ut/olap/uuid_ut.cpp create mode 100644 yql/essentials/public/uuid/ya.make create mode 100644 yql/essentials/public/uuid/yql_uuid.cpp create mode 100644 yql/essentials/public/uuid/yql_uuid.h diff --git a/ydb/core/formats/arrow/arrow_batch_builder.cpp b/ydb/core/formats/arrow/arrow_batch_builder.cpp index 815718c3c45f..8b95a2891862 100644 --- a/ydb/core/formats/arrow/arrow_batch_builder.cpp +++ b/ydb/core/formats/arrow/arrow_batch_builder.cpp @@ -46,6 +46,14 @@ arrow::Status AppendCell(arrow::Decimal128Builder& builder, const TCell& cell) { return builder.Append(cell.Data()); } +arrow::Status AppendCell(arrow::FixedSizeBinaryBuilder& builder, const TCell& cell) { + if (cell.IsNull()) { + return builder.AppendNull(); + } + Y_ABORT_UNLESS(cell.Size() == static_cast(builder.byte_width())); + return builder.Append(cell.Data()); +} + template arrow::Status AppendCell(arrow::RecordBatchBuilder& builder, const TCell& cell, ui32 colNum) { using TBuilderType = typename arrow::TypeTraits::BuilderType; diff --git a/ydb/core/formats/arrow/arrow_helpers.cpp b/ydb/core/formats/arrow/arrow_helpers.cpp index 0cc76775251b..8d45980528f4 100644 --- a/ydb/core/formats/arrow/arrow_helpers.cpp +++ b/ydb/core/formats/arrow/arrow_helpers.cpp @@ -53,6 +53,14 @@ std::shared_ptr CreateEmptyArrowImpl(const return arrow::duration(arrow::TimeUnit::TimeUnit::MICRO); } +template <> +std::shared_ptr CreateEmptyArrowImpl(const NScheme::TTypeInfo& typeInfo) { + if (typeInfo.GetTypeId() == NScheme::NTypeIds::Uuid) { + return arrow::fixed_size_binary(16); + } + return std::shared_ptr(); +} + arrow::Result> GetArrowType(NScheme::TTypeInfo typeInfo) { std::shared_ptr result; bool success = SwitchYqlTypeToArrowType(typeInfo, [&](TTypeWrapper typeHolder) { @@ -60,7 +68,7 @@ arrow::Result> GetArrowType(NScheme::TTypeInfo result = CreateEmptyArrowImpl(typeInfo); return true; }); - if (success) { + if (success && result) { return result; } @@ -79,6 +87,8 @@ arrow::Result> GetCSVArrowType(NScheme::TTypeIn case NScheme::NTypeIds::Date: case NScheme::NTypeIds::Date32: return std::make_shared(arrow::TimeUnit::SECOND); + case NScheme::NTypeIds::Uuid: + return std::make_shared(); default: return GetArrowType(typeId); } diff --git a/ydb/core/formats/arrow/converter.h b/ydb/core/formats/arrow/converter.h index 8288d50cb178..9af6039d4bee 100644 --- a/ydb/core/formats/arrow/converter.h +++ b/ydb/core/formats/arrow/converter.h @@ -57,6 +57,11 @@ class TArrowToYdbConverter { return MakeCellFromView(column, row); } + template <> + TCell MakeCell(const std::shared_ptr& column, i64 row) { + return MakeCellFromView(column, row); + } + public: static bool NeedDataConversion(const NScheme::TTypeInfo& colType); diff --git a/ydb/core/formats/arrow/switch/switch_type.h b/ydb/core/formats/arrow/switch/switch_type.h index b5183df39d9a..eae50192c286 100644 --- a/ydb/core/formats/arrow/switch/switch_type.h +++ b/ydb/core/formats/arrow/switch/switch_type.h @@ -71,6 +71,9 @@ template case NScheme::NTypeIds::Interval64: return callback(TTypeWrapper()); + case NScheme::NTypeIds::Uuid: + return callback(TTypeWrapper()); + case NScheme::NTypeIds::PairUi64Ui64: case NScheme::NTypeIds::ActorId: case NScheme::NTypeIds::StepOrderId: diff --git a/ydb/core/grpc_services/rpc_load_rows.cpp b/ydb/core/grpc_services/rpc_load_rows.cpp index e544a56f94db..88794bbdd6b8 100644 --- a/ydb/core/grpc_services/rpc_load_rows.cpp +++ b/ydb/core/grpc_services/rpc_load_rows.cpp @@ -82,9 +82,12 @@ bool ConvertArrowToYdbPrimitive(const arrow::DataType& type, Ydb::Type& toType) decimalType->set_scale(arrowDecimal->scale()); return true; } + case arrow::Type::FIXED_SIZE_BINARY: { + toType.set_type_id(Ydb::Type::UUID); + return true; + } case arrow::Type::NA: case arrow::Type::HALF_FLOAT: - case arrow::Type::FIXED_SIZE_BINARY: case arrow::Type::DATE32: case arrow::Type::DATE64: case arrow::Type::TIME32: diff --git a/ydb/core/kqp/runtime/kqp_scan_data.cpp b/ydb/core/kqp/runtime/kqp_scan_data.cpp index 01ca38d97ac1..68f09467319b 100644 --- a/ydb/core/kqp/runtime/kqp_scan_data.cpp +++ b/ydb/core/kqp/runtime/kqp_scan_data.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -286,6 +287,23 @@ class TElementAccessor { } }; +template <> +class TElementAccessor { +public: + using TArrayType = arrow::FixedSizeBinaryArray; + static void Validate(const arrow::FixedSizeBinaryArray& array) { + YQL_ENSURE(array.byte_width() == NUuid::UUID_LEN); + } + + static NYql::NUdf::TUnboxedValue ExtractValue(const arrow::FixedSizeBinaryArray& array, const ui32 rowIndex) { + auto data = array.GetView(rowIndex); + return MakeString(NUdf::TStringRef(data.data(), data.size())); + } + static TFixedWidthStatAccumulator BuildStatAccumulator(const NScheme::TTypeInfo& typeInfo) { + return TFixedWidthStatAccumulator(typeInfo); + } +}; + } template @@ -430,6 +448,10 @@ TBytesStatistics WriteColumnValuesFromArrowImpl(TAccessor editAccessor, { return WriteColumnValuesFromArrowSpecImpl>(editAccessor, batch, columnIndex, columnPtr, columnType); } + case NTypeIds::Uuid: + { + return WriteColumnValuesFromArrowSpecImpl>(editAccessor, batch, columnIndex, columnPtr, columnType); + } case NTypeIds::PairUi64Ui64: case NTypeIds::ActorId: case NTypeIds::StepOrderId: diff --git a/ydb/core/kqp/runtime/kqp_scan_data_ut.cpp b/ydb/core/kqp/runtime/kqp_scan_data_ut.cpp index a06b3f39f9e1..eaaa9437106d 100644 --- a/ydb/core/kqp/runtime/kqp_scan_data_ut.cpp +++ b/ydb/core/kqp/runtime/kqp_scan_data_ut.cpp @@ -2,6 +2,7 @@ #include #include +#include #include namespace NKikimr::NMiniKQL { @@ -44,6 +45,7 @@ struct TDataRow { {27, TTypeInfo(NPg::TypeDescFromPgTypeName("pgint8")), ""}, {28, TTypeInfo(NPg::TypeDescFromPgTypeName("pgfloat4")), ""}, {29, TTypeInfo(NPg::TypeDescFromPgTypeName("pgfloat8")), ""}, + {30, TTypeInfo(NTypeIds::Uuid), ""}, }; } @@ -77,6 +79,7 @@ struct TDataRow { i64 PgInt8; float PgFloat4; double PgFloat8; + NYql::NUuid::TUuid Uuid; static std::shared_ptr MakeArrowSchema() { std::vector> fields = { @@ -110,6 +113,7 @@ struct TDataRow { arrow::field("pgint8", arrow::int64()), arrow::field("pgfloat4", arrow::float32()), arrow::field("pgfloat8", arrow::float64()), + arrow::field("uuid", arrow::fixed_size_binary(16)), }; return std::make_shared(std::move(fields)); @@ -195,6 +199,9 @@ std::shared_ptr VectorToBatch(const std::vectorGetFieldAs(colIndex++)->Append(row.Interval64); UNIT_ASSERT(result.ok()); + } else if (colName == "uuid") { + auto result = batchBuilder->GetFieldAs(colIndex++)->Append(row.Uuid.Data); + UNIT_ASSERT(result.ok()); } else if (colName == "dec") { auto result = batchBuilder->GetFieldAs(colIndex++)->Append(reinterpret_cast(&row.Decimal)); UNIT_ASSERT(result.ok()); @@ -225,13 +232,19 @@ std::shared_ptr VectorToBatch(const std::vector(uuid.Data), false)); + return uuid; +} + TVector TestRows() { TVector rows = { - {false, -1, -1, -1, -1, 1, 1, 1, 1, -1.0f, -1.0, "s1" , "u1" , "{j:1}", "{y:1}", 0, 0, 0, 0, -1, -1, -1, -1, 111, 1111, -21, 210, -2100, 21.3f, 21.6}, - {false, 2, 2, 2, 2, 2, 2, 2, 2, 2.0f, 2.0, "s2" , "u2" , "{j:2}", "{y:2}", 0, 0, 0, 0, -2, -2, -2, -2, 222, 2222, 22, -220, 2200, -22.3f, 22.6}, - {false, -3, -3, -3, -3, 3, 3, 3, 3, -3.0f, -3.0, "s3" , "u3" , "{j:3}", "{y:3}", 0, 0, 0, 0, -3, -3, -3, -3, 333, 3333, 23, 230, -2300, 23.3f, -23.6}, - {false, -4, -4, -4, -4, 4, 4, 4, 4, 4.0f, 4.0, "s4" , "u4" , "{j:4}", "{y:4}", 0, 0, 0, 0, -4, -4, -4, -4, 444, 4444, -24, 240, 2400, -24.3f, 24.6}, - {false, -5, -5, -5, -5, 5, 5, 5, 5, 5.0f, 5.0, "long5long5long5long5long5", "utflong5utflong5utflong5", "{j:5}", "{y:5}", 0, 0, 0, 0, -5, -5, -5, -5, 555, 5555, 25, -250, 2500, 25.3f, -25.6}, + {false, -1, -1, -1, -1, 1, 1, 1, 1, -1.0f, -1.0, "s1" , "u1" , "{j:1}", "{y:1}", 0, 0, 0, 0, -1, -1, -1, -1, 111, 1111, -21, 210, -2100, 21.3f, 21.6, MakeUuid("00000011-0022-0033-0044-000000000055")}, + {false, 2, 2, 2, 2, 2, 2, 2, 2, 2.0f, 2.0, "s2" , "u2" , "{j:2}", "{y:2}", 0, 0, 0, 0, -2, -2, -2, -2, 222, 2222, 22, -220, 2200, -22.3f, 22.6, MakeUuid("00001100-2200-3300-4400-000000005500")}, + {false, -3, -3, -3, -3, 3, 3, 3, 3, -3.0f, -3.0, "s3" , "u3" , "{j:3}", "{y:3}", 0, 0, 0, 0, -3, -3, -3, -3, 333, 3333, 23, 230, -2300, 23.3f, -23.6, MakeUuid("00110000-0033-0044-0000-000000550000")}, + {false, -4, -4, -4, -4, 4, 4, 4, 4, 4.0f, 4.0, "s4" , "u4" , "{j:4}", "{y:4}", 0, 0, 0, 0, -4, -4, -4, -4, 444, 4444, -24, 240, 2400, -24.3f, 24.6, MakeUuid("11000000-3300-4400-0000-000055000000")}, + {false, -5, -5, -5, -5, 5, 5, 5, 5, 5.0f, 5.0, "long5long5long5long5long5", "utflong5utflong5utflong5", "{j:5}", "{y:5}", 0, 0, 0, 0, -5, -5, -5, -5, 555, 5555, 25, -250, 2500, 25.3f, -25.6, MakeUuid("00000033-0044-0000-0000-005500000011")}, }; return rows; } @@ -252,6 +265,7 @@ Y_UNIT_TEST_SUITE(TKqpScanData) { NUdf::TStringValue str(pattern.size()); std::memcpy(str.Data(), pattern.data(), pattern.size()); NUdf::TUnboxedValue containsLongString(NUdf::TUnboxedValuePod(std::move(str))); + NYql::NUuid::TUuid uuid(MakeUuid("00000000-4400-3300-2200-000000000011")); NYql::NDecimal::TInt128 decimalVal = 123456789012; NYql::NDecimal::TInt128 decimal35Val = 987654321012; TVector cases = { @@ -303,6 +317,7 @@ Y_UNIT_TEST_SUITE(TKqpScanData) { {NUdf::TUnboxedValuePod::Embedded("{j:0}"), NTypeIds::Json , {16, 8 } }, {NUdf::TUnboxedValuePod::Embedded("{y:0}"), NTypeIds::Yson , {16, 8 } }, {containsLongString , NTypeIds::String, {16 + pattern.size(), pattern.size()}}, + {NUdf::TUnboxedValuePod(uuid ), NTypeIds::Uuid, {16 + 16, 16}}, {NUdf::TUnboxedValuePod( ), TTypeInfo(NPg::TypeDescFromPgTypeName("pgint2" )), {16, 8 } }, {NUdf::TUnboxedValuePod( ), TTypeInfo(NPg::TypeDescFromPgTypeName("pgint4" )), {16, 8 } }, {NUdf::TUnboxedValuePod( ), TTypeInfo(NPg::TypeDescFromPgTypeName("pgint8" )), {16, 8 } }, @@ -374,6 +389,7 @@ Y_UNIT_TEST_SUITE(TKqpScanData) { UNIT_ASSERT_EQUAL(container[27].Get(), row.PgInt8 ); UNIT_ASSERT_EQUAL(container[28].Get(), row.PgFloat4 ); UNIT_ASSERT_EQUAL(container[29].Get(), row.PgFloat8 ); + UNIT_ASSERT_EQUAL(container[30].Get(), row.Uuid ); } UNIT_ASSERT(scanData.IsEmpty()); diff --git a/ydb/core/kqp/ut/arrow/kqp_types_arrow_ut.cpp b/ydb/core/kqp/ut/arrow/kqp_types_arrow_ut.cpp index 55492178d546..376b69a0769f 100644 --- a/ydb/core/kqp/ut/arrow/kqp_types_arrow_ut.cpp +++ b/ydb/core/kqp/ut/arrow/kqp_types_arrow_ut.cpp @@ -48,6 +48,7 @@ void InsertAllColumnsAndCheckSelectAll(TKikimrRunner* runner) { YsonValue Yson, JsonDocumentValue JsonDocument, DyNumberValue DyNumber, + UuidValue Uuid, Int32NotNullValue Int32 NOT NULL, PRIMARY KEY (Key) ); @@ -56,18 +57,18 @@ void InsertAllColumnsAndCheckSelectAll(TKikimrRunner* runner) { auto insertResult = session.ExecuteDataQuery(R"( --!syntax_v1 - INSERT INTO `/Root/Tmp` (Key, BoolValue, Int32Value, Uint32Value, Int64Value, Uint64Value, FloatValue, DoubleValue, StringValue, Utf8Value, DateValue, DatetimeValue, TimestampValue, IntervalValue, DecimalValue, JsonValue, YsonValue, JsonDocumentValue, DyNumberValue, Int32NotNullValue) VALUES - (42, true, -1, 1, -2, 2, CAST(3.0 AS Float), 4.0, "five", Utf8("six"), Date("2007-07-07"), Datetime("2008-08-08T08:08:08Z"), Timestamp("2009-09-09T09:09:09.09Z"), Interval("P10D"), CAST("11.11" AS Decimal(22, 9)), "[12]", "[13]", JsonDocument("[14]"), DyNumber("15.15"), 123); + INSERT INTO `/Root/Tmp` (Key, BoolValue, Int32Value, Uint32Value, Int64Value, Uint64Value, FloatValue, DoubleValue, StringValue, Utf8Value, DateValue, DatetimeValue, TimestampValue, IntervalValue, DecimalValue, JsonValue, YsonValue, JsonDocumentValue, DyNumberValue, UuidValue, Int32NotNullValue) VALUES + (42, true, -1, 1, -2, 2, CAST(3.0 AS Float), 4.0, "five", Utf8("six"), Date("2007-07-07"), Datetime("2008-08-08T08:08:08Z"), Timestamp("2009-09-09T09:09:09.09Z"), Interval("P10D"), CAST("11.11" AS Decimal(22, 9)), "[12]", "[13]", JsonDocument("[14]"), DyNumber("15.15"), Uuid("00000000-0000-0000-0000-000000000011"), 123); )", TTxControl::BeginTx().CommitTx()).GetValueSync(); UNIT_ASSERT_C(insertResult.IsSuccess(), insertResult.GetIssues().ToString()); - auto it = db.StreamExecuteScanQuery("SELECT Key, BoolValue, Int32Value, Uint32Value, Int64Value, Uint64Value, FloatValue, DoubleValue, StringValue, Utf8Value, DateValue, DatetimeValue, TimestampValue, IntervalValue, DecimalValue, JsonValue, YsonValue, JsonDocumentValue, DyNumberValue, Int32NotNullValue FROM `/Root/Tmp`").GetValueSync(); + auto it = db.StreamExecuteScanQuery("SELECT Key, BoolValue, Int32Value, Uint32Value, Int64Value, Uint64Value, FloatValue, DoubleValue, StringValue, Utf8Value, DateValue, DatetimeValue, TimestampValue, IntervalValue, DecimalValue, JsonValue, YsonValue, JsonDocumentValue, DyNumberValue, UuidValue, Int32NotNullValue FROM `/Root/Tmp`").GetValueSync(); UNIT_ASSERT_C(it.IsSuccess(), it.GetIssues().ToString()); auto streamPart = it.ReadNext().GetValueSync(); UNIT_ASSERT_C(streamPart.IsSuccess(), streamPart.GetIssues().ToString()); auto resultSet = streamPart.ExtractResultSet(); auto columns = resultSet.GetColumnsMeta(); - UNIT_ASSERT_C(columns.size() == 20, "Wrong columns count"); + UNIT_ASSERT_C(columns.size() == 21, "Wrong columns count"); NYdb::TResultSetParser parser(resultSet); UNIT_ASSERT_C(parser.TryNextRow(), "Row is missing"); UNIT_ASSERT(parser.ColumnParser(0).GetOptionalUint64().value() == 42); @@ -90,7 +91,8 @@ void InsertAllColumnsAndCheckSelectAll(TKikimrRunner* runner) { UNIT_ASSERT(parser.ColumnParser(16).GetOptionalYson().value() == "[13]"); UNIT_ASSERT(parser.ColumnParser(17).GetOptionalJsonDocument().value() == "[14]"); UNIT_ASSERT(parser.ColumnParser(18).GetOptionalDyNumber().value() == ".1515e2"); - UNIT_ASSERT(parser.ColumnParser(19).GetInt32() == 123); + UNIT_ASSERT(parser.ColumnParser(19).GetOptionalUuid().value().ToString() == "00000000-0000-0000-0000-000000000011"); + UNIT_ASSERT(parser.ColumnParser(20).GetInt32() == 123); } } diff --git a/ydb/core/kqp/ut/common/columnshard.cpp b/ydb/core/kqp/ut/common/columnshard.cpp index f44ce45889b5..c285fee582a7 100644 --- a/ydb/core/kqp/ut/common/columnshard.cpp +++ b/ydb/core/kqp/ut/common/columnshard.cpp @@ -5,6 +5,7 @@ #include #include #include +#include extern "C" { #include @@ -392,6 +393,8 @@ namespace NKqp { return arrow::field(name, arrow::binary(), nullable); case NScheme::NTypeIds::Decimal: return arrow::field(name, arrow::decimal(typeInfo.GetDecimalType().GetPrecision(), typeInfo.GetDecimalType().GetScale()), nullable); + case NScheme::NTypeIds::Uuid: + return arrow::field(name, arrow::fixed_size_binary(NUuid::UUID_LEN), nullable); case NScheme::NTypeIds::Pg: switch (NPg::PgTypeIdFromTypeDesc(typeInfo.GetPgTypeDesc())) { case INT2OID: diff --git a/ydb/core/kqp/ut/olap/uuid_ut.cpp b/ydb/core/kqp/ut/olap/uuid_ut.cpp new file mode 100644 index 000000000000..ebb5ff14aa3f --- /dev/null +++ b/ydb/core/kqp/ut/olap/uuid_ut.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace NKikimr { +namespace NKqp { + +using namespace NYdb; +using namespace NYdb::NTable; + +Y_UNIT_TEST_SUITE(KqpUuidColumnShard) { + + Y_UNIT_TEST(UseUuidColumns) { + TKikimrSettings runnerSettings; + runnerSettings.WithSampleTables = false; + TTestHelper testHelper(runnerSettings); + + const TVector schema = { + TTestHelper::TColumnSchema().SetName("id").SetType(NScheme::NTypeIds::Int32).SetNullable(false), + TTestHelper::TColumnSchema().SetName("int").SetType(NScheme::NTypeIds::Int64), + TTestHelper::TColumnSchema().SetName("uuid").SetType(NScheme::NTypeIds::Uuid) + }; + + Tests::NCommon::TLoggerInit(testHelper.GetKikimr()).Initialize(); + TTestHelper::TColumnTable testTable; + + testTable.SetName("/Root/ColumnTableTest").SetPrimaryKey({"id"}).SetSharding({"id"}).SetSchema(schema); + testHelper.CreateTable(testTable); + + { + TTestHelper::TUpdatesBuilder tableInserter(testTable.GetArrowSchema(schema)); + tableInserter.AddRow().Add(1).Add(3).Add(TUuidValue("d034f360-423d-4c8c-ba6f-a07607c29c9f")); + tableInserter.AddRow().Add(2).Add(4).AddNull(); + testHelper.BulkUpsert(testTable, tableInserter); + } + + testHelper.ReadData("SELECT * FROM `/Root/ColumnTableTest` WHERE id=1", "[[1;[3];[\"d034f360-423d-4c8c-ba6f-a07607c29c9f\"]]]"); + testHelper.ReadData("SELECT * FROM `/Root/ColumnTableTest` WHERE id=2", "[[2;[4];#]]"); + } + + Y_UNIT_TEST(UseUuidAsPrimaryKey) { + TKikimrSettings runnerSettings; + runnerSettings.WithSampleTables = false; + TTestHelper testHelper(runnerSettings); + + const TVector schema = { + TTestHelper::TColumnSchema().SetName("uuid").SetType(NScheme::NTypeIds::Uuid).SetNullable(false), + TTestHelper::TColumnSchema().SetName("int").SetType(NScheme::NTypeIds::Int64) + }; + + Tests::NCommon::TLoggerInit(testHelper.GetKikimr()).Initialize(); + TTestHelper::TColumnTable testTable; + + testTable.SetName("/Root/ColumnTableTest").SetPrimaryKey({"uuid"}).SetSharding({"uuid"}).SetSchema(schema); + testHelper.CreateTable(testTable); + + { + // UUIDs are sorted by internal binary representation + TTestHelper::TUpdatesBuilder tableInserter(testTable.GetArrowSchema(schema)); + tableInserter.AddRow().Add(TUuidValue("00000000-0000-0000-0000-000000000000")).AddNull(); + tableInserter.AddRow().Add(TUuidValue("00000000-0000-0000-0000-000000000011")).Add(123); + tableInserter.AddRow().Add(TUuidValue("00000000-0000-2200-0000-000000000000")).Add(-456); + tableInserter.AddRow().Add(TUuidValue("00000000-0000-0033-0000-000000000000")).Add(-10); + tableInserter.AddRow().Add(TUuidValue("00000044-0000-0000-0000-000000000000")).Add(9999); + testHelper.BulkUpsert(testTable, tableInserter); + } + + testHelper.ReadData("SELECT * FROM `/Root/ColumnTableTest` WHERE `uuid` = Uuid('00000000-0000-0000-0000-000000000011')", + "[[[123];\"00000000-0000-0000-0000-000000000011\"]]"); + testHelper.ReadData("SELECT * FROM `/Root/ColumnTableTest` WHERE `uuid` != Uuid('00000000-0000-2200-0000-000000000000')", + "[[#;\"00000000-0000-0000-0000-000000000000\"];[[123];\"00000000-0000-0000-0000-000000000011\"];" + "[[-10];\"00000000-0000-0033-0000-000000000000\"];[[9999];\"00000044-0000-0000-0000-000000000000\"]]"); + testHelper.ReadData("SELECT * FROM `/Root/ColumnTableTest` WHERE `uuid` < Uuid('00000000-0000-2200-0000-000000000000')", + "[[#;\"00000000-0000-0000-0000-000000000000\"];[[123];\"00000000-0000-0000-0000-000000000011\"]]"); + testHelper.ReadData("SELECT * FROM `/Root/ColumnTableTest` WHERE `uuid` > Uuid('00000000-0000-0033-0000-000000000000')", + "[[[9999];\"00000044-0000-0000-0000-000000000000\"]]"); + testHelper.ReadData("SELECT * FROM `/Root/ColumnTableTest` WHERE `uuid` <= Uuid('00000000-0000-0000-0000-000000000011')", + "[[#;\"00000000-0000-0000-0000-000000000000\"];[[123];\"00000000-0000-0000-0000-000000000011\"]]"); + testHelper.ReadData("SELECT * FROM `/Root/ColumnTableTest` WHERE `uuid` >= Uuid('00000000-0000-0033-0000-000000000000')", + "[[[-10];\"00000000-0000-0033-0000-000000000000\"];[[9999];\"00000044-0000-0000-0000-000000000000\"]]"); + } + + Y_UNIT_TEST(UuidCsv) { + TKikimrSettings runnerSettings; + runnerSettings.WithSampleTables = false; + + TTestHelper testHelper(runnerSettings); + Tests::NCommon::TLoggerInit(testHelper.GetKikimr()).SetComponents({ NKikimrServices::GROUPED_MEMORY_LIMITER }, "CS").Initialize(); + + TVector schema = { + TTestHelper::TColumnSchema().SetName("id").SetType(NScheme::NTypeIds::Int64).SetNullable(false), + TTestHelper::TColumnSchema().SetName("uuid").SetType(NScheme::NTypeIds::Uuid) + }; + + TTestHelper::TColumnTable testTable; + testTable.SetName("/Root/ColumnTableTest").SetPrimaryKey({"id"}).SetSharding({"id"}).SetSchema(schema); + testHelper.CreateTable(testTable); + { + TStringBuilder builder; + builder << "1,3ee41410-16f4-48cd-bc04-29a6cfef46d4" << Endl; + const auto result = testHelper.GetKikimr().GetTableClient().BulkUpsert(testTable.GetName(), EDataFormat::CSV, builder).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess() , result.GetIssues().ToString()); + } + testHelper.ReadData("SELECT id, uuid FROM `/Root/ColumnTableTest` WHERE id=1", "[[1;[\"3ee41410-16f4-48cd-bc04-29a6cfef46d4\"]]]"); + } + +} + +} // namespace NKqp +} // namespace NKikimr diff --git a/ydb/core/kqp/ut/olap/ya.make b/ydb/core/kqp/ut/olap/ya.make index 2347af96b75d..3c23d3718cba 100644 --- a/ydb/core/kqp/ut/olap/ya.make +++ b/ydb/core/kqp/ut/olap/ya.make @@ -30,6 +30,7 @@ SRCS( statistics_ut.cpp sys_view_ut.cpp tiering_ut.cpp + uuid_ut.cpp write_ut.cpp ) diff --git a/ydb/core/kqp/ut/scheme/kqp_scheme_ut.cpp b/ydb/core/kqp/ut/scheme/kqp_scheme_ut.cpp index 2c8f147de94a..63205bb2b3a5 100644 --- a/ydb/core/kqp/ut/scheme/kqp_scheme_ut.cpp +++ b/ydb/core/kqp/ut/scheme/kqp_scheme_ut.cpp @@ -11891,7 +11891,7 @@ Y_UNIT_TEST_SUITE(KqpOlapScheme) { TVector schema = { TTestHelper::TColumnSchema().SetName("id").SetType(NScheme::NTypeIds::Int32).SetNullable(false), - TTestHelper::TColumnSchema().SetName("level").SetType(NScheme::NTypeIds::Uuid).SetNullable(true) + TTestHelper::TColumnSchema().SetName("level").SetType(NScheme::NTypeIds::DyNumber).SetNullable(true) }; TTestHelper::TColumnTable testTable; testTable.SetName("/Root/ColumnTableTest").SetPrimaryKey({"id"}).SetSharding({"id"}).SetSchema(schema); diff --git a/ydb/core/tx/columnshard/test_helper/columnshard_ut_common.h b/ydb/core/tx/columnshard/test_helper/columnshard_ut_common.h index 38f878b400ed..ab6fdc339b11 100644 --- a/ydb/core/tx/columnshard/test_helper/columnshard_ut_common.h +++ b/ydb/core/tx/columnshard/test_helper/columnshard_ut_common.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -540,6 +541,13 @@ class TTableUpdatesBuilder { return true; } } + if constexpr (std::is_same::value) { + if constexpr (arrow::is_fixed_size_binary_type::value) { + Y_ABORT_UNLESS(typedBuilder.byte_width() == NUuid::UUID_LEN); + Y_ABORT_UNLESS(typedBuilder.Append(data.Buf_.Bytes).ok()); + return true; + } + } Y_ABORT("Unknown type combination"); return false; })); diff --git a/ydb/core/tx/columnshard/transactions/operators/schema.cpp b/ydb/core/tx/columnshard/transactions/operators/schema.cpp index 53446e2d9b4c..fb926c7fd03f 100644 --- a/ydb/core/tx/columnshard/transactions/operators/schema.cpp +++ b/ydb/core/tx/columnshard/transactions/operators/schema.cpp @@ -181,7 +181,8 @@ NKikimr::TConclusionStatus TSchemaTransactionOperator::ValidateTableSchema(const //NTypeIds::Double, NTypeIds::String, NTypeIds::Utf8, - NTypeIds::Decimal + NTypeIds::Decimal, + NTypeIds::Uuid }; if (!schema.KeyColumnNamesSize()) { diff --git a/ydb/core/tx/schemeshard/olap/columns/update.cpp b/ydb/core/tx/schemeshard/olap/columns/update.cpp index cec015c10e4d..0e9e5daf88fa 100644 --- a/ydb/core/tx/schemeshard/olap/columns/update.cpp +++ b/ydb/core/tx/schemeshard/olap/columns/update.cpp @@ -336,6 +336,7 @@ bool TOlapColumnBase::IsAllowedPkType(ui32 typeId) { case NYql::NProto::Timestamp64: case NYql::NProto::Interval64: case NYql::NProto::Decimal: + case NYql::NProto::Uuid: return true; default: return false; diff --git a/ydb/library/formats/arrow/csv/converter/csv_arrow.cpp b/ydb/library/formats/arrow/csv/converter/csv_arrow.cpp index 383f77a9a0eb..9aacd6088506 100644 --- a/ydb/library/formats/arrow/csv/converter/csv_arrow.cpp +++ b/ydb/library/formats/arrow/csv/converter/csv_arrow.cpp @@ -1,9 +1,11 @@ #include "csv_arrow.h" #include +#include #include #include #include +#include #include namespace NKikimr::NFormats { @@ -102,6 +104,24 @@ namespace { return *res; } + std::shared_ptr ConvertStringToUuidArray(std::shared_ptr data) { + auto originalArr = std::make_shared(data); + arrow::FixedSizeBinaryBuilder builder(arrow::fixed_size_binary(NUuid::UUID_LEN)); + Y_ABORT_UNLESS(builder.Reserve(originalArr->length()).ok()); + for (long i = 0; i < originalArr->length(); ++i) { + if (originalArr->IsNull(i)) { + Y_ABORT_UNLESS(builder.AppendNull().ok()); + } else { + ui16 dw[8]; + Y_ABORT_UNLESS(NUuid::ParseUuidToArray(originalArr->Value(i), dw, false)); + builder.UnsafeAppend(arrow::Buffer(reinterpret_cast(dw), sizeof(dw))); + } + } + auto res = builder.Finish(); + Y_ABORT_UNLESS(res.ok()); + return *res; + } + } std::shared_ptr TArrowCSV::ConvertColumnTypes(std::shared_ptr parsedBatch) const { @@ -145,6 +165,8 @@ std::shared_ptr TArrowCSV::ConvertColumnTypes(std::shared_pt Y_ABORT_UNLESS(false); } }()); + } else if (fArr->type()->id() == arrow::StringType::type_id && originalType->id() == arrow::FixedSizeBinaryType::type_id) { + resultColumns.emplace_back(ConvertStringToUuidArray(fArr->data())); } else { Y_ABORT_UNLESS(false); } diff --git a/ydb/library/formats/arrow/switch/compare.h b/ydb/library/formats/arrow/switch/compare.h index 9729d3f06f7a..16499039179c 100644 --- a/ydb/library/formats/arrow/switch/compare.h +++ b/ydb/library/formats/arrow/switch/compare.h @@ -39,6 +39,7 @@ class TComparator { case arrow::Type::BINARY: return CompareView(lhs, lpos, rhs, rpos); case arrow::Type::FIXED_SIZE_BINARY: + return CompareView(lhs, lpos, rhs, rpos); case arrow::Type::DATE32: case arrow::Type::DATE64: break; diff --git a/ydb/tests/stress/olap_workload/workload/__init__.py b/ydb/tests/stress/olap_workload/workload/__init__.py index 7dc9aeec3654..8185447d65be 100644 --- a/ydb/tests/stress/olap_workload/workload/__init__.py +++ b/ydb/tests/stress/olap_workload/workload/__init__.py @@ -22,7 +22,7 @@ "String", "Utf8", - # Uuid", https://github.com/ydb-platform/ydb/issues/13047 + "Uuid", # https://github.com/ydb-platform/ydb/issues/13047 "Date", "Datetime", diff --git a/yql/essentials/minikql/arrow/arrow_util.h b/yql/essentials/minikql/arrow/arrow_util.h index ce9e75d4695c..be39a05c60fe 100644 --- a/yql/essentials/minikql/arrow/arrow_util.h +++ b/yql/essentials/minikql/arrow/arrow_util.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace NKikimr::NMiniKQL { @@ -199,6 +200,31 @@ struct TPrimitiveDataType { }; }; +template<> +struct TPrimitiveDataType { + using TLayout = NYql::NUuid::TUuid; + + class TResult: public arrow::FixedSizeBinaryType + { + public: + TResult(): arrow::FixedSizeBinaryType(NUuid::UUID_LEN) + { } + }; + + + class TScalarResult: public arrow::FixedSizeBinaryScalar + { + public: + TScalarResult(std::shared_ptr value) + : arrow::FixedSizeBinaryScalar(std::move(value), arrow::fixed_size_binary(NUuid::UUID_LEN)) + { } + + TScalarResult() + : arrow::FixedSizeBinaryScalar(arrow::fixed_size_binary(NUuid::UUID_LEN)) + { } + }; +}; + template ::value>::type> inline arrow::Datum MakeScalarDatum(T value) { return arrow::Datum(std::make_shared::TScalarResult>(value)); diff --git a/yql/essentials/minikql/mkql_type_builder.cpp b/yql/essentials/minikql/mkql_type_builder.cpp index 51181edbd7fa..79fe0a306751 100644 --- a/yql/essentials/minikql/mkql_type_builder.cpp +++ b/yql/essentials/minikql/mkql_type_builder.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -1544,7 +1545,8 @@ bool ConvertArrowTypeImpl(NUdf::EDataSlot slot, std::shared_ptr return true; } case NUdf::EDataSlot::Uuid: { - return false; + type = arrow::fixed_size_binary(NUuid::UUID_LEN); + return true; } case NUdf::EDataSlot::Decimal: { type = arrow::fixed_size_binary(sizeof(NYql::NUdf::TUnboxedValuePod)); diff --git a/yql/essentials/public/udf/arrow/block_item.h b/yql/essentials/public/udf/arrow/block_item.h index 79686b3094f7..1e8d8395c8ac 100644 --- a/yql/essentials/public/udf/arrow/block_item.h +++ b/yql/essentials/public/udf/arrow/block_item.h @@ -5,6 +5,9 @@ #include #include +#include +#include + namespace NYql::NUdf { class TBlockItem { @@ -28,6 +31,12 @@ class TBlockItem { Raw.Simple.Meta = static_cast(EMarkers::Embedded); } + inline explicit TBlockItem(const NYql::NUuid::TUuid& value) { + Raw.StringRef.Value = value.Data; + Raw.StringRef.Size = sizeof(value); + Raw.StringValue.Meta = static_cast(EMarkers::String); + } + inline explicit TBlockItem(IBoxedValuePtr&& value) { Raw.Resource.Meta = static_cast(EMarkers::Boxed); Raw.Resource.Value = value.Release(); @@ -86,10 +95,10 @@ class TBlockItem { } // TODO: deprecate As() in favor of Get() - template ::Result>> + template ::Result || std::is_same_v>> inline T As() const; - template ::Result>> + template ::Result || std::is_same_v>> inline T Get() const; inline NYql::NDecimal::TInt128 GetInt128() const { @@ -100,6 +109,12 @@ class TBlockItem { return v; } + inline NYql::NUuid::TUuid GetUuid() const { + TStringRef asStr = AsStringRef(); + Y_DEBUG_ABORT_UNLESS(asStr.size() == sizeof(NYql::NUuid::TUuid)); + return *reinterpret_cast(asStr.data()); + } + // TODO: deprecate AsTuple() in favor of GetElements() inline const TBlockItem* AsTuple() const { Y_DEBUG_ABORT_UNLESS(GetMarkers() == EMarkers::Embedded); @@ -252,6 +267,16 @@ class TBlockItem { UDF_ASSERT_TYPE_SIZE(TBlockItem, 16); +template <> +inline NYql::NUuid::TUuid TBlockItem::As() const { + return GetUuid(); +} + +template <> +inline NYql::NUuid::TUuid TBlockItem::Get() const { + return GetUuid(); +} + #define VALUE_AS(xType) \ template <> \ inline xType TBlockItem::As() const \ diff --git a/yql/essentials/public/udf/arrow/block_item_hasher.h b/yql/essentials/public/udf/arrow/block_item_hasher.h index 9108d7b06e8f..bb243e6c0a1e 100644 --- a/yql/essentials/public/udf/arrow/block_item_hasher.h +++ b/yql/essentials/public/udf/arrow/block_item_hasher.h @@ -57,6 +57,14 @@ class TFixedSizeBlockItemHasher : public TBlo } }; +template +class TFixedSizeBlockItemHasher : public TBlockItemHasherBase, Nullable> { +public: + ui64 DoHash(TBlockItem value) const { + return GetValueHash::Slot>(NUdf::TUnboxedValuePod(value.GetUuid())); + } +}; + template class TTzDateBlockItemHasher : public TBlockItemHasherBase, Nullable> { public: diff --git a/yql/essentials/public/udf/arrow/block_reader.h b/yql/essentials/public/udf/arrow/block_reader.h index 25a3dbe47758..d067678df52d 100644 --- a/yql/essentials/public/udf/arrow/block_reader.h +++ b/yql/essentials/public/udf/arrow/block_reader.h @@ -75,7 +75,7 @@ class TFixedSizeBlockReaderBase : public TBlockReaderBase { } } - if constexpr(std::is_same_v) { + if constexpr(std::is_same_v || std::is_same_v) { auto& fixedScalar = checked_cast(scalar); T value; memcpy((void*)&value, fixedScalar.value->data(), sizeof(T)); return static_cast(this)->MakeBlockItem(value); @@ -126,7 +126,7 @@ class TFixedSizeBlockReaderBase : public TBlockReaderBase { out.PushChar(1); } - if constexpr(std::is_same_v) { + if constexpr(std::is_same_v || std::is_same_v) { auto& fixedScalar = arrow::internal::checked_cast(scalar); T value; memcpy((void*)&value, fixedScalar.value->data(), sizeof(T)); out.PushNumber(value); diff --git a/yql/essentials/public/udf/arrow/dispatch_traits.h b/yql/essentials/public/udf/arrow/dispatch_traits.h index f49b2861630c..0b5177f43c15 100644 --- a/yql/essentials/public/udf/arrow/dispatch_traits.h +++ b/yql/essentials/public/udf/arrow/dispatch_traits.h @@ -69,9 +69,9 @@ std::unique_ptr MakeTzDateArrowTraitsImpl(bool isOpti } } -template -concept CanInstantiateArrowTraitsForDecimal = requires { - typename TTraits::template TFixedSize; +template +concept CanInstantiateArrowTraits = requires { + typename TTraits::template TFixedSize; }; template @@ -201,13 +201,19 @@ std::unique_ptr DispatchByArrowTraits(const ITypeInfo case NUdf::EDataSlot::TzTimestamp64: return MakeTzDateArrowTraitsImpl(isOptional, type, std::forward(args)...); case NUdf::EDataSlot::Decimal: { - if constexpr (CanInstantiateArrowTraitsForDecimal) { + if constexpr (CanInstantiateArrowTraits) { return MakeFixedSizeArrowTraitsImpl(isOptional, type, std::forward(args)...); } else { Y_ENSURE(false, "Unsupported data slot"); } } - case NUdf::EDataSlot::Uuid: + case NUdf::EDataSlot::Uuid: { + if constexpr (CanInstantiateArrowTraits) { + return MakeFixedSizeArrowTraitsImpl(isOptional, type, std::forward(args)...); + } else { + Y_ENSURE(false, "Unsupported data slot"); + } + } case NUdf::EDataSlot::DyNumber: Y_ENSURE(false, "Unsupported data slot"); } diff --git a/yql/essentials/public/udf/udf_value.h b/yql/essentials/public/udf/udf_value.h index a895d0f8ea70..4fc89aef0c05 100644 --- a/yql/essentials/public/udf/udf_value.h +++ b/yql/essentials/public/udf/udf_value.h @@ -7,6 +7,7 @@ #include "udf_version.h" #include +#include #include // FAIL, VERIFY_DEBUG #include // Min, Max @@ -808,7 +809,7 @@ friend class TUnboxedValue; inline bool IsEmbedded() const { return EMarkers::Embedded == Raw.GetMarkers(); } // Data accessors - template ::Result || std::is_same_v>> + template ::Result || std::is_same_v || std::is_same_v>> inline T Get() const; template ::Result>> inline T GetOrDefault(T ifEmpty) const; @@ -818,6 +819,9 @@ friend class TUnboxedValue; inline NYql::NDecimal::TInt128 GetInt128() const; inline NYql::NDecimal::TUint128 GetUint128() const; + inline explicit TUnboxedValuePod(NYql::NUuid::TUuid value); + inline NYql::NUuid::TUuid GetUuid() const; + inline const void* GetRawPtr() const; inline void* GetRawPtr(); diff --git a/yql/essentials/public/udf/udf_value_inl.h b/yql/essentials/public/udf/udf_value_inl.h index 43c4c84140f6..915b9d5932bc 100644 --- a/yql/essentials/public/udf/udf_value_inl.h +++ b/yql/essentials/public/udf/udf_value_inl.h @@ -757,6 +757,12 @@ inline NYql::NDecimal::TInt128 TUnboxedValuePod::Get() return GetInt128(); } +template <> +inline NYql::NUuid::TUuid TUnboxedValuePod::Get() const +{ + return GetUuid(); +} + template <> inline TUnboxedValuePod::TUnboxedValuePod(bool value) { @@ -794,6 +800,19 @@ inline TUnboxedValuePod::TUnboxedValuePod(NYql::NDecimal::TUint128 value) Raw.Simple.Meta = static_cast(EMarkers::Embedded); } +inline TUnboxedValuePod::TUnboxedValuePod(NYql::NUuid::TUuid value): + TUnboxedValuePod(TStringValue(TStringRef(value.Data, sizeof(value)))) +{ +} + +inline NYql::NUuid::TUuid TUnboxedValuePod::GetUuid() const { + UDF_VERIFY(EMarkers::Empty != Raw.GetMarkers(), "Value is empty."); + const auto strRef = AsStringRef(); + UDF_VERIFY(strRef.Size() == sizeof(NYql::NUuid::TUuid), "Uuid size mismatch."); + NYql::NUuid::TUuid value(strRef.Data(), strRef.Size()); + return value; +} + inline const void* TUnboxedValuePod::GetRawPtr() const { return &Raw; diff --git a/yql/essentials/public/uuid/ya.make b/yql/essentials/public/uuid/ya.make new file mode 100644 index 000000000000..7cc1ba9df5f1 --- /dev/null +++ b/yql/essentials/public/uuid/ya.make @@ -0,0 +1,8 @@ +LIBRARY() + +SRCS( + yql_uuid.h + yql_uuid.cpp +) + +END() diff --git a/yql/essentials/public/uuid/yql_uuid.cpp b/yql/essentials/public/uuid/yql_uuid.cpp new file mode 100644 index 000000000000..831810ae4077 --- /dev/null +++ b/yql/essentials/public/uuid/yql_uuid.cpp @@ -0,0 +1 @@ +#include "yql_uuid.h" diff --git a/yql/essentials/public/uuid/yql_uuid.h b/yql/essentials/public/uuid/yql_uuid.h new file mode 100644 index 000000000000..fd7b06daf26d --- /dev/null +++ b/yql/essentials/public/uuid/yql_uuid.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace NYql { +namespace NUuid { + +struct TUuid { + char Data[16]; + + TUuid() { + std::memset(Data, 0, sizeof(Data)); + } + + template + TUuid(const T* data, size_t size) { + if (size != sizeof(Data)) { + ythrow yexception() << "TUuid: expected " << sizeof(Data) << ", got " << size << " bytes"; + } + std::memcpy(Data, data, sizeof(Data)); + } + + bool operator==(const TUuid& rhs) const { + return std::memcmp(Data, rhs.Data, sizeof(Data)) == 0; + } + bool operator<(const TUuid& rhs) const { + return std::memcmp(Data, rhs.Data, sizeof(Data)) < 0; + } + bool operator>(const TUuid& rhs) const { + return std::memcmp(Data, rhs.Data, sizeof(Data)) > 0; + } +}; + +} +} diff --git a/yql/essentials/public/ya.make b/yql/essentials/public/ya.make index d53fabdd192b..2db5d097b57d 100644 --- a/yql/essentials/public/ya.make +++ b/yql/essentials/public/ya.make @@ -8,5 +8,6 @@ RECURSE( sql_format types udf + uuid ) diff --git a/yt/yql/providers/yt/codec/yt_arrow_converter.cpp b/yt/yql/providers/yt/codec/yt_arrow_converter.cpp index 52ce6e8cea34..e42d18c6038e 100644 --- a/yt/yql/providers/yt/codec/yt_arrow_converter.cpp +++ b/yt/yql/providers/yt/codec/yt_arrow_converter.cpp @@ -600,8 +600,9 @@ struct TComplexTypeYsonReaderTraits { using TResult = IYsonComplexTypeReader; template using TTuple = TTupleYsonReader; - // TODO: Implement reader for decimals - template && (std::is_integral_v || std::is_floating_point_v)>> + // TODO: Implement reader for decimals and uuids + template && !std::is_same_v && (std::is_integral_v || std::is_floating_point_v)>> using TFixedSize = TFixedSizeYsonReader; template using TStrings = TStringYsonReader;