Skip to content

Commit ea50281

Browse files
committed
Fix KeyValueMetadata::DeleteMany crash with duplicate indices
1 parent d433ffc commit ea50281

2 files changed

Lines changed: 41 additions & 0 deletions

File tree

cpp/src/arrow/util/key_value_metadata.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,17 @@ Status KeyValueMetadata::Delete(int64_t index) {
114114
Status KeyValueMetadata::DeleteMany(std::vector<int64_t> indices) {
115115
std::sort(indices.begin(), indices.end());
116116
const int64_t size = static_cast<int64_t>(keys_.size());
117+
if (ARROW_PREDICT_FALSE(!indices.empty() &&
118+
(indices.front() < 0 || indices.back() >= size))) {
119+
const int64_t out_of_bounds = indices.front() < 0 ? indices.front() : indices.back();
120+
return Status::IndexError("KeyValueMetadata::DeleteMany: index ", out_of_bounds,
121+
" is out of bounds for metadata "
122+
"of size ",
123+
size);
124+
}
125+
// Remove duplicate indices, otherwise the shifting logic below would move
126+
// entries to incorrect (potentially negative) positions (GH-50351)
127+
indices.erase(std::unique(indices.begin(), indices.end()), indices.end());
117128
indices.push_back(size);
118129

119130
int64_t shift = 0;

cpp/src/arrow/util/key_value_metadata_test.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,36 @@ TEST(KeyValueMetadataTest, Delete) {
226226
ASSERT_OK(metadata.DeleteMany({}));
227227
ASSERT_TRUE(metadata.Equals(KeyValueMetadata({"bb", "dd", "ee"}, {"2", "4", "5"})));
228228
}
229+
{
230+
// GH-50351: duplicate indices must not crash and are treated as one
231+
KeyValueMetadata metadata(keys, values);
232+
ASSERT_OK(metadata.DeleteMany({0, 0}));
233+
ASSERT_TRUE(metadata.Equals(KeyValueMetadata({"bb", "cc", "dd", "ee", "ff", "gg"},
234+
{"2", "3", "4", "5", "6", "7"})));
235+
236+
ASSERT_OK(metadata.DeleteMany({1, 3, 1, 3, 1}));
237+
ASSERT_TRUE(metadata.Equals(
238+
KeyValueMetadata({"bb", "dd", "ff", "gg"}, {"2", "4", "6", "7"})));
239+
}
240+
{
241+
// GH-50351: out-of-bounds indices must error without modifying metadata
242+
KeyValueMetadata metadata(keys, values);
243+
std::string expected_error_message =
244+
"Index error: KeyValueMetadata::DeleteMany: index 7 is out of bounds for "
245+
"metadata "
246+
"of size 7";
247+
ASSERT_RAISES_WITH_MESSAGE(IndexError, expected_error_message,
248+
metadata.DeleteMany({7}));
249+
expected_error_message =
250+
"Index error: KeyValueMetadata::DeleteMany: index -1 is out of bounds for "
251+
"metadata "
252+
"of size 7";
253+
ASSERT_RAISES_WITH_MESSAGE(IndexError, expected_error_message,
254+
metadata.DeleteMany({-1}));
255+
ASSERT_RAISES(IndexError, metadata.DeleteMany({0, 3, 7}));
256+
ASSERT_RAISES(IndexError, metadata.DeleteMany({-1, 0, 3}));
257+
ASSERT_TRUE(metadata.Equals(KeyValueMetadata(keys, values)));
258+
}
229259
}
230260

231261
} // namespace arrow

0 commit comments

Comments
 (0)