-
Notifications
You must be signed in to change notification settings - Fork 847
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
nullif
incorrectly calculates null_count
, sometimes panics with substraction overflow error
#3579
Comments
Just in case, here's the code that I used to validate that the quick fix works: std::panic::set_hook(Box::new(|_info| { /* silence panics */ }));
for n in 0..=128+64 {
let left = Int32Array::from((0..n as i32).into_iter().collect::<Vec<_>>());
for somes in 0..=n {
for trues in 0..=somes {
let right = BooleanArray::from((0..n).into_iter().map(|i| {
Some(i < trues).filter(|_| i < somes)
}).collect::<Vec<_>>());
let ok = std::panic::catch_unwind(|| {
let arr = arrow_select::nullif::nullif(&left, &right).unwrap();
let arr: &Int32Array = arrow_array::cast::as_primitive_array(&arr);
arrow_array::Array::data(arr).validate_full().unwrap();
}).is_ok();
if !ok {
println!("{n} {somes} {trues}");
}
}
}
} |
Thank you for the detailed report, makes sense to me. The change to FnMut was relatively recent so I think this should be the only impacted location, but I will verify. Bit embarrassing this kernel has taken quite so many iterations to fix 😅 |
A potential bug I found whilst testing this, dating from 2020 - #3589 Edit: I was mistaken |
|
Describe the bug
nullif(left, right)
incorrectly calculatesnull_count
for the result array, wheneverleft
doesn't have a null_buffer and haslen % 64 == 0
. It can even panic, if there are less than 64 true values inright
.To Reproduce
Expected behavior
It works.
Actual behavior
It panics with 'attempt to subtract with overflow' at this line:
arrow-rs/arrow-select/src/nullif.rs
Line 107 in 796b670
Additional context
The problem evaded fixes #3034 and #3263, which were incomplete. The wrong calculation occurs in these lines:
arrow-rs/arrow-select/src/nullif.rs
Lines 81 to 90 in 796b670
The reason it is wrong is the un-intuitive, if not to say wrong, behavior of
bitwise_unary_op_helper()
and friends. They're callingop()
unconditionally on the remainder bits, even if there are none:arrow-rs/arrow-buffer/src/buffer/ops.rs
Lines 119 to 123 in 3a90654
It doesn't make a difference for the produced output buffer, because it is then just extended with slice of 0 bytes, i.e. remains unaffected. But it does matter for FnMut closures with side effects, such as the one found in
nullif
, which, as a result of this behavior, adds an excess of 64 tovalid_count
, when there are no remainder bits, i.e. bit length is a multiple of 64.The quick fix is to do
valid_count -= 64 - remainder_len;
innullif()
unconditionally, i.e. just remove theif remainder_len != 0 {
condition around it. It was clearly written in assumption, thatbitwise_unary_op_helper()
doesn't callop()
in excess, when there are no remainder bits.A better fix could have been to avoid making excess
op()
calls inbitwise_unary_op_helper()
and friends, but since they're public, it could lead to bugs in external consumers, which might have come to rely on this weird behavior.In any case, I suggest at least checking whether other usages of
bitwise_unary_op_helper()
and friendsin arrow-rs make the same incorrect assumption. Temporarily changing the type of
op
parameter fromFnMut
toFn
and looking at the compilation error fallout should point out all suspicious places.The text was updated successfully, but these errors were encountered: