In v0.18.0, when using the new FlatArray type for zero-copy arrays, if the array is Toasted data, even just referencing it in a parameter will cause a segfault. I have a failing test for replication here https://github.com/mhov/pgx/tree/flat_arr_toast_fail
cargo test --features pg18,pg_test --no-default-features borrow_test_arr_data_ptr_toasted
#[pg_extern]
fn borrow_get_arr_nelems(arr: &FlatArray<'_, i32>) -> libc::c_int {
arr.nelems() as _
}
#[pg_test]
fn borrow_test_arr_data_ptr_toasted() {
// 2000 i32s should be the TOAST threshold.
Spi::run("CREATE TEMP TABLE test_array (arr integer[])")
.expect("failed to create temp table");
Spi::run("INSERT INTO test_array SELECT array_agg(i) FROM generate_series(1, 2500) i")
.expect("failed to insert into temp table");
let len = Spi::get_one::<i32>("SELECT borrow_get_arr_nelems(arr) FROM test_array");
assert_eq!(len, Ok(Some(2500)));
}
This will fix it, but I'm 0% confident in the soundness of de-toasting here. As always I'd defer to @workingjubilee
unsafe impl<T: ?Sized> BorrowDatum for FlatArray<'_, T> {
const PASS: layout::PassBy = layout::PassBy::Ref;
unsafe fn point_from(ptr: ptr::NonNull<u8>) -> ptr::NonNull<Self> {
unsafe {
+ let toast = ptr::NonNull::new(pg_sys::pg_detoast_datum(ptr.as_ptr().cast())).unwrap();
let len =
- varlena::varsize_any(ptr.as_ptr().cast()) - mem::size_of::<pg_sys::ArrayType>();
+ varlena::varsize_any(toast.as_ptr().cast()) - mem::size_of::<pg_sys::ArrayType>();
ptr::NonNull::new_unchecked(
- ptr::slice_from_raw_parts_mut(ptr.as_ptr(), len) as *mut Self
+ ptr::slice_from_raw_parts_mut(toast.as_ptr(), len) as *mut Self
)
}
}
}
In v0.18.0, when using the new FlatArray type for zero-copy arrays, if the array is Toasted data, even just referencing it in a parameter will cause a segfault. I have a failing test for replication here https://github.com/mhov/pgx/tree/flat_arr_toast_fail
This will fix it, but I'm 0% confident in the soundness of de-toasting here. As always I'd defer to @workingjubilee
unsafe impl<T: ?Sized> BorrowDatum for FlatArray<'_, T> { const PASS: layout::PassBy = layout::PassBy::Ref; unsafe fn point_from(ptr: ptr::NonNull<u8>) -> ptr::NonNull<Self> { unsafe { + let toast = ptr::NonNull::new(pg_sys::pg_detoast_datum(ptr.as_ptr().cast())).unwrap(); let len = - varlena::varsize_any(ptr.as_ptr().cast()) - mem::size_of::<pg_sys::ArrayType>(); + varlena::varsize_any(toast.as_ptr().cast()) - mem::size_of::<pg_sys::ArrayType>(); ptr::NonNull::new_unchecked( - ptr::slice_from_raw_parts_mut(ptr.as_ptr(), len) as *mut Self + ptr::slice_from_raw_parts_mut(toast.as_ptr(), len) as *mut Self ) } } }