diff --git a/clickhouse/columns/tuple.h b/clickhouse/columns/tuple.h index 5bf3b0d6..b6b0bbc7 100644 --- a/clickhouse/columns/tuple.h +++ b/clickhouse/columns/tuple.h @@ -71,12 +71,22 @@ class ColumnTupleT : public ColumnTuple { ColumnTupleT(std::tuple...> columns) : ColumnTuple(TupleToVector(columns)), typed_columns_(std::move(columns)) {} + ColumnTupleT(std::tuple...> columns, std::vector names) + : ColumnTuple(TupleToVector(columns), std::move(names)), typed_columns_(std::move(columns)) {} + ColumnTupleT(std::vector columns) : ColumnTuple(columns), typed_columns_(VectorToTuple(std::move(columns))) {} + ColumnTupleT(std::vector columns, std::vector names) + : ColumnTuple(columns, std::move(names)), typed_columns_(VectorToTuple(std::move(columns))) {} + ColumnTupleT(const std::initializer_list columns) : ColumnTuple(columns), typed_columns_(VectorToTuple(std::move(columns))) {} + ColumnTupleT(std::initializer_list columns, std::vector names) + : ColumnTuple(std::vector(columns), std::move(names)) + , typed_columns_(VectorToTuple(std::vector(columns))) {} + inline ValueType At(size_t index) const { return GetTupleOfValues(index); } inline ValueType operator[](size_t index) const { return GetTupleOfValues(index); } @@ -101,7 +111,8 @@ class ColumnTupleT : public ColumnTuple { if (col.TupleSize() != std::tuple_size_v) { throw ValidationError("Can't wrap from " + col.GetType().GetName()); } - return std::make_shared>(VectorToTuple(std::move(col))); + auto names = col.Type()->As()->GetItemNames(); + return std::make_shared>(VectorToTuple(std::move(col)), std::move(names)); } static auto Wrap(Column&& col) { return Wrap(std::move(dynamic_cast(col))); } diff --git a/ut/columns_ut.cpp b/ut/columns_ut.cpp index 13a1731c..72ecad74 100644 --- a/ut/columns_ut.cpp +++ b/ut/columns_ut.cpp @@ -1139,6 +1139,53 @@ TEST(ColumnsCase, ColumnTupleT_Empty) { EXPECT_EQ(col.Size(), 0u); } +TEST(ColumnsCase, ColumnTupleT_WithNames) { + using TestTuple = ColumnTupleT; + + TestTuple col( + std::make_tuple( + std::make_shared(), + std::make_shared() + ), + std::vector{"id", "name"} + ); + EXPECT_EQ(col.Type()->GetName(), "Tuple(id UInt64, name String)"); + + col.Append(std::make_tuple(uint64_t(42), std::string("hello"))); + EXPECT_EQ(col.At(0), std::make_tuple(uint64_t(42), std::string_view("hello"))); +} + +TEST(ColumnsCase, ColumnTupleT_Wrap_PreservesNames) { + ColumnTuple base( + {std::make_shared(), std::make_shared()}, + {"id", "name"} + ); + + using TestTuple = ColumnTupleT; + auto wrapped = TestTuple::Wrap(std::move(base)); + EXPECT_EQ(wrapped->Type()->GetName(), "Tuple(id UInt64, name String)"); +} + +TEST(ColumnsCase, ColumnTupleT_Slice_PreservesNames) { + using TestTuple = ColumnTupleT; + + auto col = std::make_shared( + std::make_tuple( + std::make_shared(), + std::make_shared() + ), + std::vector{"id", "name"} + ); + col->Append(std::make_tuple(uint64_t(1), std::string("a"))); + col->Append(std::make_tuple(uint64_t(2), std::string("b"))); + + auto sliced = col->Slice(0, 1); + EXPECT_EQ(sliced->Type()->GetName(), "Tuple(id UInt64, name String)"); + + auto cloned = col->CloneEmpty(); + EXPECT_EQ(cloned->Type()->GetName(), "Tuple(id UInt64, name String)"); +} + TEST(ColumnsCase, ColumnMapT) { ColumnMapT col( std::make_shared(),