This is an automated email from the ASF dual-hosted git repository.

pitrou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/main by this push:
     new a4e3909bcf GH-51029: [C++] Fix MapArray validation with unknown null 
count (#51093)
a4e3909bcf is described below

commit a4e3909bcf59911316a696990f1eb1d583cddae1
Author: Anurag Tryambak Raut <[email protected]>
AuthorDate: Wed Sep 2 19:01:28 2026 +0530

    GH-51029: [C++] Fix MapArray validation with unknown null count (#51093)
    
    ### Rationale for this change
    
    `MapArray` validation uses `MayHaveNulls()` to check that the map child and 
keys contain no nulls. When an all-valid validity bitmap is present with an 
unknown null count, `MayHaveNulls()` can report that the array may contain 
nulls even though all values are valid.
    
    During a cast of a `MapArray`, this can cause validation to return an 
invalid status which is passed to `ARROW_CHECK_OK`, resulting in a process 
abort instead of a recoverable error.
    
    ### What changes are included in this PR?
    
    - Use `GetNullCount()` instead of `MayHaveNulls()` when validating the 
`MapArray` child and keys.
    - Add a regression test covering an all-valid validity bitmap with 
`kUnknownNullCount`.
    
    ### Are these changes tested?
    
    Yes, with existing and additional unit tests.
    
    ### Are there any user-facing changes?
    
    Only a bugfix.
    
    ### This PR contains a "Critical Fix".
    
    This fixes a bug that can cause a process crash when casting a valid 
`MapArray` whose keys carry an all-valid validity bitmap with an unknown null 
count. This is easily reproduced in Python:
    ```python
    import pyarrow as pa
    m = pa.array([{"a": "1"}, {"b": "2"}], type=pa.map_(pa.string(), 
pa.string()))
    m.filter(pa.array([False, True])).cast(pa.map_(pa.large_string(), 
pa.large_string()))
    ```
    
    * GitHub Issue: #51029
    
    Lead-authored-by: Anurag Raut <[email protected]>
    Co-authored-by: Antoine Pitrou <[email protected]>
    Signed-off-by: Antoine Pitrou <[email protected]>
---
 cpp/src/arrow/array/array_list_test.cc | 37 ++++++++++++++++++++++++++++++++++
 cpp/src/arrow/array/array_nested.cc    | 10 ++++-----
 2 files changed, 42 insertions(+), 5 deletions(-)

diff --git a/cpp/src/arrow/array/array_list_test.cc 
b/cpp/src/arrow/array/array_list_test.cc
index 8406bd1d8e..9908eb5886 100644
--- a/cpp/src/arrow/array/array_list_test.cc
+++ b/cpp/src/arrow/array/array_list_test.cc
@@ -1532,6 +1532,43 @@ TEST_F(TestMapArray, ValueBuilder) {
   ASSERT_ARRAYS_EQUAL(*actual_list, map_as_list);
 }
 
+// GH-51029: Validate MapArray keys with an all-valid bitmap and unknown null 
count.
+TEST_F(TestMapArray, ValidateKeysWithAllValidBitmap) {
+  auto keys = ArrayFromJSON(utf8(), R"(["a", "b"])");
+  auto items = ArrayFromJSON(int32(), "[1, 2]");
+  auto offsets = ArrayFromJSON(int32(), "[0, 1, 2]");
+
+  // Inject an all-valid validity bitmap with kUnknownNullCount.
+  auto keys_data = keys->data()->Copy();
+  keys_data->buffers[0] = ArrayFromJSON(boolean(), "[true, 
true]")->data()->buffers[1];
+  keys_data->null_count = kUnknownNullCount;
+
+  ASSERT_OK_AND_ASSIGN(auto result,
+                       MapArray::FromArrays(offsets, MakeArray(keys_data), 
items));
+  ASSERT_OK(result->ValidateFull());
+  ASSERT_EQ(result->length(), 2);
+}
+
+TEST_F(TestMapArray, FromArraysWithAllValidOffsetsBitmap) {
+  auto offsets = ArrayFromJSON(int32(), "[0, 1, 2]");
+  auto keys = ArrayFromJSON(utf8(), R"(["a", "b"])");
+  auto items = ArrayFromJSON(int32(), "[1, 2]");
+
+  // Inject an all-valid validity bitmap with kUnknownNullCount.
+  auto offsets_data = offsets->data()->Copy();
+  offsets_data->buffers[0] =
+      ArrayFromJSON(boolean(), "[true, true, true]")->data()->buffers[1];
+  offsets_data->null_count = kUnknownNullCount;
+  offsets = MakeArray(offsets_data);
+
+  auto null_bitmap = ArrayFromJSON(boolean(), "[true, 
true]")->data()->buffers[1];
+
+  ASSERT_OK_AND_ASSIGN(auto result,
+                       MapArray::FromArrays(offsets, keys, items, pool_, 
null_bitmap));
+  ASSERT_OK(result->ValidateFull());
+  ASSERT_EQ(result->length(), 2);
+}
+
 // ----------------------------------------------------------------------
 // FixedSizeList tests
 
diff --git a/cpp/src/arrow/array/array_nested.cc 
b/cpp/src/arrow/array/array_nested.cc
index c5a26a475c..55bce4d0de 100644
--- a/cpp/src/arrow/array/array_nested.cc
+++ b/cpp/src/arrow/array/array_nested.cc
@@ -115,7 +115,7 @@ Result<std::shared_ptr<typename 
TypeTraits<TYPE>::ArrayType>> ListArrayFromArray
     return Status::TypeError("List offsets must be ", 
OffsetArrowType::type_name());
   }
 
-  if (null_bitmap != nullptr && offsets.data()->MayHaveNulls()) {
+  if (null_bitmap != nullptr && offsets.data()->GetNullCount() != 0) {
     return Status::Invalid(
         "Ambiguous to specify both validity map and offsets with nulls");
   }
@@ -826,7 +826,7 @@ Result<std::shared_ptr<Array>> MapArray::FromArraysInternal(
     return Status::Invalid("Map key and item arrays must be equal length");
   }
 
-  if (null_bitmap != nullptr && offsets->data()->MayHaveNulls()) {
+  if (null_bitmap != nullptr && offsets->data()->GetNullCount() != 0) {
     return Status::Invalid(
         "Ambiguous to specify both validity map and offsets with nulls");
   }
@@ -835,7 +835,7 @@ Result<std::shared_ptr<Array>> MapArray::FromArraysInternal(
     return Status::NotImplemented("Null bitmap with offsets slice not 
supported.");
   }
 
-  if (offsets->data()->MayHaveNulls()) {
+  if (offsets->data()->GetNullCount() != 0) {
     ARROW_ASSIGN_OR_RAISE(auto buffers,
                           CleanListOffsets<MapType>(NULLPTR, *offsets, pool));
     return std::make_shared<MapArray>(type, offsets->length() - 1, 
std::move(buffers),
@@ -896,13 +896,13 @@ Status MapArray::ValidateChildData(
   if (pair_data->type->id() != Type::STRUCT) {
     return Status::Invalid("Map array child array should have struct type");
   }
-  if (pair_data->MayHaveNulls()) {
+  if (pair_data->GetNullCount() != 0) {
     return Status::Invalid("Map array child array should have no nulls");
   }
   if (pair_data->child_data.size() != 2) {
     return Status::Invalid("Map array child array should have two fields");
   }
-  if (pair_data->child_data[0]->MayHaveNulls()) {
+  if (pair_data->child_data[0]->GetNullCount() != 0) {
     return Status::Invalid("Map array keys array should have no nulls");
   }
   return Status::OK();

Reply via email to