From a685dd5422d7d00330b816613722b473ddff4e25 Mon Sep 17 00:00:00 2001 From: Johannes Misch Date: Tue, 3 Jun 2025 12:38:59 +0200 Subject: [PATCH 1/2] Introduce validation to the ColumnArray constructor The ColumnArray constructor would just accept incompatible value- and offset-arrays and the client would then happily send them, causing issues in the ClickHouse server. This introduces validation of the arrays. --- clickhouse/columns/array.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/clickhouse/columns/array.cpp b/clickhouse/columns/array.cpp index 1867d778..02d81c14 100644 --- a/clickhouse/columns/array.cpp +++ b/clickhouse/columns/array.cpp @@ -15,6 +15,20 @@ ColumnArray::ColumnArray(ColumnRef data, std::shared_ptr offsets) , data_(data) , offsets_(offsets) { + const auto rows = offsets_->Size(); + const auto expected_values = rows > 0 ? offsets_->At(rows - 1) : 0; + std::uint64_t prev = 0; + // Ensure the offset array is internally consistent + for (const auto& o : offsets_->GetWritableData()) { + if (o < prev) { + throw ValidationError("offsets must be monotonically increasing"); + } + prev = o; + } + if (data_->Size() != expected_values) { + throw ValidationError("Mismatch between data and offsets: Expected " + std::to_string(expected_values) + + " values in data, but got " + std::to_string(data_->Size())); + } } ColumnArray::ColumnArray(ColumnArray&& other) From a842ab85eb78a81b0199950b4268f4df40a909a5 Mon Sep 17 00:00:00 2001 From: Johannes Misch Date: Tue, 17 Jun 2025 19:47:03 +0200 Subject: [PATCH 2/2] Fix ColumnArray slicing and single array data constructor --- clickhouse/columns/array.cpp | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/clickhouse/columns/array.cpp b/clickhouse/columns/array.cpp index 02d81c14..94163c4d 100644 --- a/clickhouse/columns/array.cpp +++ b/clickhouse/columns/array.cpp @@ -5,9 +5,17 @@ namespace clickhouse { -ColumnArray::ColumnArray(ColumnRef data) - : ColumnArray(data, std::make_shared()) -{ +namespace { +std::shared_ptr make_single_offset(size_t value) { + auto res = std::make_shared(); + if (value != 0) { + res->Append(value); + } + return res; +} +} // namespace + +ColumnArray::ColumnArray(ColumnRef data) : ColumnArray(data, make_single_offset(data->Size())) { } ColumnArray::ColumnArray(ColumnRef data, std::shared_ptr offsets) @@ -55,11 +63,15 @@ ColumnRef ColumnArray::Slice(size_t begin, size_t size) const { if (size && begin + size > Size()) throw ValidationError("Slice indexes are out of bounds"); - auto result = std::make_shared(data_->Slice(GetOffset(begin), GetOffset(begin + size) - GetOffset(begin))); - for (size_t i = 0; i < size; i++) - result->AddOffset(GetSize(begin + i)); + auto sliced_data = data_->Slice(GetOffset(begin), GetOffset(begin + size) - GetOffset(begin)); + auto offsets = std::make_shared(); + auto offset = uint64_t{0}; + for (size_t i = 0; i < size; i++) { + offset += GetSize(begin + i); + offsets->Append(offset); + } - return result; + return std::make_shared(std::move(sliced_data), std::move(offsets)); } ColumnRef ColumnArray::CloneEmpty() const {