Skip to content

[C++] fill_null_forward, fill_null_backward and replace_with_mask read the wrong bits on a sliced boolean array #51223

Description

@singhpratech

Describe the bug

On a boolean array with a non-zero offset (a slice), fill_null_forward, fill_null_backward and
replace_with_mask return values that belong to other positions of the parent array: the value
bitmap is read without the slice's offset. A materialised copy of the same slice gives the right
answer, and an int64 slice is handled correctly, so it is specific to the bit-packed boolean path.

pyarrow 25.0.1, Python 3.13.9, macOS 26.6 (arm64), wheel from PyPI.

import pyarrow as pa, pyarrow.compute as pc

full = pa.array([True, False, None, True, True, False, None, True], pa.bool_())
sliced = full.slice(3, 5)                       # [True, True, False, None, True]
copy = pa.array(sliced.to_pylist(), pa.bool_())  # same values, offset 0

print("forward  sliced", pc.fill_null_forward(sliced).to_pylist())
print("forward  copy  ", pc.fill_null_forward(copy).to_pylist())
print("backward sliced", pc.fill_null_backward(sliced).to_pylist())
print("backward copy  ", pc.fill_null_backward(copy).to_pylist())

mask = pa.array([True, False, False, False, False])
repl = pa.array([False], pa.bool_())
print("replace  sliced", pc.replace_with_mask(sliced, mask, repl).to_pylist())
print("replace  copy  ", pc.replace_with_mask(copy, mask, repl).to_pylist())

print("int64 control  ", pc.fill_null_forward(pa.array([1, None, 3, 4, None, 6, None, 8]).slice(3, 5)).to_pylist())

Output:

forward  sliced [True, False, False, False, True]
forward  copy   [True, True, False, False, True]
backward sliced [True, False, False, True, True]
backward copy   [True, True, False, True, True]
replace  sliced [False, False, False, None, True]
replace  copy   [False, True, False, None, True]
int64 control   [4, 4, 6, 6, 8]

In every "sliced" line the second element is False, which is full[1], not full[4] (True):
the untouched positions are copied from bit index i of the parent bitmap instead of offset + i.

Expected behavior

The "sliced" and "copy" lines agree, as they do for int64.

This looks distinct from #45086 (an allocation crash on a boolean ChunkedArray, fixed on main by #50843 and not in a release yet): the values here are wrong, not the allocation.

Component(s)

C++, Python

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions