diff --git a/cpp/src/arrow/compute/kernels/vector_replace.cc b/cpp/src/arrow/compute/kernels/vector_replace.cc index 1a3e743784d4..ae39977aaea5 100644 --- a/cpp/src/arrow/compute/kernels/vector_replace.cc +++ b/cpp/src/arrow/compute/kernels/vector_replace.cc @@ -217,15 +217,15 @@ struct ReplaceMaskImpl> { static Result ExecScalarMask(KernelContext* ctx, const ArraySpan& array, const BooleanScalar& mask, ExecValue replacements, int64_t replacements_offset, ExecResult* out) { - out->value = array; - return Status::OK(); + out->value = array.ToArrayData(); + return replacements_offset; } static Result ExecArrayMask(KernelContext* ctx, const ArraySpan& array, const ArraySpan& mask, int64_t mask_offset, ExecValue replacements, int64_t replacements_offset, ExecResult* out) { - out->value = array; - return Status::OK(); + out->value = array.ToArrayData(); + return replacements_offset; } }; diff --git a/cpp/src/arrow/compute/kernels/vector_replace_test.cc b/cpp/src/arrow/compute/kernels/vector_replace_test.cc index 587b9f2a60eb..9dc8e70ab6a2 100644 --- a/cpp/src/arrow/compute/kernels/vector_replace_test.cc +++ b/cpp/src/arrow/compute/kernels/vector_replace_test.cc @@ -199,6 +199,13 @@ class TestReplaceBoolean : public TestReplaceKernel { } }; +class TestReplaceNull : public TestReplaceKernel { + protected: + std::shared_ptr type() override { + return TypeTraits::type_singleton(); + } +}; + class TestReplaceFixedSizeBinary : public TestReplaceKernel { protected: std::shared_ptr type() override { return fixed_size_binary(3); } @@ -538,6 +545,35 @@ TEST_F(TestReplaceBoolean, ReplaceWithMask) { } } +TEST_F(TestReplaceNull, ReplaceWithMask) { + std::vector cases = { + {this->array("[]"), this->mask_scalar(false), this->array("[]"), this->array("[]")}, + {this->array("[]"), this->mask_scalar(true), this->array("[]"), this->array("[]")}, + {this->array("[]"), this->null_mask_scalar(), this->array("[]"), this->array("[]")}, + + {this->array("[null]"), this->mask_scalar(false), this->array("[]"), + this->array("[null]")}, + + {this->array("[null]"), this->mask_scalar(true), this->array("[null]"), + this->array("[null]")}, + + {this->array("[null]"), this->null_mask_scalar(), this->array("[]"), + this->array("[null]")}, + + {this->array("[null, null]"), this->mask("[false, false]"), this->array("[]"), + this->array("[null, null]")}, + {this->array("[null, null]"), this->mask("[true, true]"), + this->array("[null, null]"), this->array("[null, null]")}, + {this->array("[null, null]"), this->mask("[null, null]"), this->array("[]"), + this->array("[null, null]")}, + }; + + for (auto test_case : cases) { + this->Assert(ReplaceWithMask, test_case.input, test_case.mask, test_case.replacements, + test_case.expected); + } +} + TEST_F(TestReplaceBoolean, ReplaceWithMaskErrors) { EXPECT_RAISES_WITH_MESSAGE_THAT( Invalid, diff --git a/python/pyarrow/tests/test_compute.py b/python/pyarrow/tests/test_compute.py index 4e44a912d965..1c82d6c944bd 100644 --- a/python/pyarrow/tests/test_compute.py +++ b/python/pyarrow/tests/test_compute.py @@ -1269,6 +1269,34 @@ def test_extract_regex_span(): assert struct.tolist() == expected +def test_replace_with_mask_null_type(): + # GH-47447: replace_with_mask crashed for null type arrays + input = pa.array([None], pa.null()) + replacements = pa.array([None], pa.null()) + + result = pc.replace_with_mask(input, True, replacements) + assert result.type == pa.null() + result.validate(full=True) + assert result.to_pylist() == [None] + + result = pc.replace_with_mask(input, False, replacements) + assert result.type == pa.null() + result.validate(full=True) + assert result.to_pylist() == [None] + + mask = pa.array([True]) + result = pc.replace_with_mask(input, mask, replacements) + assert result.type == pa.null() + result.validate(full=True) + assert result.to_pylist() == [None] + + mask = pa.array([False]) + result = pc.replace_with_mask(input, mask, replacements) + assert result.type == pa.null() + result.validate(full=True) + assert result.to_pylist() == [None] + + def test_binary_join(): ar_list = pa.array([['foo', 'bar'], None, []]) expected = pa.array(['foo-bar', None, ''])