diff --git a/cpp/src/arrow/util/key_value_metadata.cc b/cpp/src/arrow/util/key_value_metadata.cc index 67bf02b4e1a..95f0b806ff1 100644 --- a/cpp/src/arrow/util/key_value_metadata.cc +++ b/cpp/src/arrow/util/key_value_metadata.cc @@ -112,6 +112,9 @@ Status KeyValueMetadata::Delete(int64_t index) { } Status KeyValueMetadata::DeleteMany(std::vector indices) { + if (indices.size() == 1) { + return Delete(indices[0]); + } std::sort(indices.begin(), indices.end()); const int64_t size = static_cast(keys_.size()); indices.push_back(size); @@ -121,11 +124,23 @@ Status KeyValueMetadata::DeleteMany(std::vector indices) { ++shift; const auto start = indices[i] + 1; const auto stop = indices[i + 1]; - DCHECK_GE(start, 0); - DCHECK_LE(start, size); - DCHECK_GE(stop, 0); - DCHECK_LE(stop, size); + + if (ARROW_PREDICT_TRUE(start < 0 || start > size)) { + return Status::IndexError("KeyValueMetadata::DeleteMany: Start index ", start - 1, + " out of bounds for metadata of size ", size); + } + + if (ARROW_PREDICT_TRUE(stop < 0 || stop > size)) { + return Status::IndexError("KeyValueMetadata::DeleteMany: Stop index ", stop, + " out of bounds for metadata of size ", size); + } + for (int64_t index = start; index < stop; ++index) { + if (ARROW_PREDICT_TRUE(index < shift)) { + return Status::IndexError("KeyValueMetadata::DeleteMany: duplicate index ", index, + " in indices to delete"); + } + keys_[index - shift] = std::move(keys_[index]); values_[index - shift] = std::move(values_[index]); } diff --git a/cpp/src/arrow/util/key_value_metadata.h b/cpp/src/arrow/util/key_value_metadata.h index 57ade11e758..b65bbb3dfd4 100644 --- a/cpp/src/arrow/util/key_value_metadata.h +++ b/cpp/src/arrow/util/key_value_metadata.h @@ -50,6 +50,12 @@ class ARROW_EXPORT KeyValueMetadata { // Note that deleting may invalidate known indices Status Delete(std::string_view key); Status Delete(int64_t index); + + /// \brief Delete metadata entries at specified index in keys and values array + /// \param indices Vector of distinct indices identifying the entries to + /// remove from the metadata. + /// \return Status indicating success or failure. + Status DeleteMany(std::vector indices); Status Set(std::string key, std::string value); diff --git a/cpp/src/arrow/util/key_value_metadata_test.cc b/cpp/src/arrow/util/key_value_metadata_test.cc index 7c021429ac1..946b363277e 100644 --- a/cpp/src/arrow/util/key_value_metadata_test.cc +++ b/cpp/src/arrow/util/key_value_metadata_test.cc @@ -226,6 +226,47 @@ TEST(KeyValueMetadataTest, Delete) { ASSERT_OK(metadata.DeleteMany({})); ASSERT_TRUE(metadata.Equals(KeyValueMetadata({"bb", "dd", "ee"}, {"2", "4", "5"}))); } + { + KeyValueMetadata metadata(keys, values); + + std::string expected_error_message = + "Index error: KeyValueMetadata::DeleteMany: Start index -3 out of bounds for " + "metadata of size 7"; + + ASSERT_RAISES_WITH_MESSAGE(IndexError, expected_error_message, + metadata.DeleteMany({-2, -3})); + } + + { + KeyValueMetadata metadata(keys, values); + + std::string expected_error_message = + "Index error: KeyValueMetadata::Delete: index -1 is out of bounds for metadata " + "of size 7"; + + ASSERT_RAISES_WITH_MESSAGE(IndexError, expected_error_message, + metadata.DeleteMany({-1})); + } + + { + KeyValueMetadata metadata(keys, values); + + std::string expected_error_message = + "Index error: KeyValueMetadata::DeleteMany: Stop index 8 out of bounds for " + "metadata of size 7"; + + ASSERT_RAISES_WITH_MESSAGE(IndexError, expected_error_message, + metadata.DeleteMany({0, 8})); + } + { + KeyValueMetadata metadata(keys, values); + std::string expected_error_message = + "Index error: KeyValueMetadata::DeleteMany: duplicate index 1 in indices to " + "delete"; + + ASSERT_RAISES_WITH_MESSAGE(IndexError, expected_error_message, + metadata.DeleteMany({0, 0, 5, 2})); + } } } // namespace arrow