From 52b142cbbd278229a3ac4d815dd26af0f119ec9e Mon Sep 17 00:00:00 2001 From: mhov Date: Sun, 22 Jun 2025 16:05:39 -0700 Subject: [PATCH 1/7] Adding support for contrib/intarray style fast-allocated arrays --- pgrx-tests/src/tests/array_tests.rs | 86 ++++++++++++ pgrx/src/array.rs | 60 ++++++++- pgrx/src/array/port.rs | 19 +++ pgrx/src/callconv.rs | 15 ++- pgrx/src/datum/array.rs | 200 +++++++++++++++++++++++++++- 5 files changed, 375 insertions(+), 5 deletions(-) diff --git a/pgrx-tests/src/tests/array_tests.rs b/pgrx-tests/src/tests/array_tests.rs index 4cc3c2e47f..ad18db2f0a 100644 --- a/pgrx-tests/src/tests/array_tests.rs +++ b/pgrx-tests/src/tests/array_tests.rs @@ -183,6 +183,11 @@ fn validate_cstring_array<'a>( Ok(true) } +#[pg_extern] +fn int_array_roundtrip(arr: Array) -> Array { + arr +} + #[cfg(any(test, feature = "pg_test"))] #[pgrx::pg_schema] mod tests { @@ -506,4 +511,85 @@ mod tests { Ok(()) } + + #[pg_test] + fn test_int_array_roundtrip_test() -> Result<(), Box> { + let a = Spi::get_one::>("SELECT int_array_roundtrip(ARRAY[1, 2, 3, 4, 5])")?; + + assert_eq!(a, Some(vec![1, 2, 3, 4, 5])); + + Ok(()) + } + + #[pg_test] + fn test_array_new_from_slice() -> Result<(), Box> { + let a = Spi::get_one::>("SELECT ARRAY[1, 2, 3, 4, 5]::\"char\"[]")? + .expect("spi result was NULL"); + let b = Array::::new_from_slice(&[1, 2, 3, 4, 5]).expect("failed to create array"); + + assert_eq!(a.as_slice()?, b.as_slice()?); + + let a = Spi::get_one::>("SELECT ARRAY[1, 2, 3, 4, 5]::smallint[]")? + .expect("spi result was NULL"); + let b = Array::::new_from_slice(&[1, 2, 3, 4, 5]).expect("failed to create array"); + + assert_eq!(a.as_slice()?, b.as_slice()?); + + let a = Spi::get_one::>("SELECT ARRAY[1, 2, 3, 4, 5]::integer[]")? + .expect("spi result was NULL"); + let b = Array::::new_from_slice(&[1, 2, 3, 4, 5]).expect("failed to create array"); + + assert_eq!(a.as_slice()?, b.as_slice()?); + + let a = Spi::get_one::>("SELECT ARRAY[1, 2, 3, 4, 5]::bigint[]")? + .expect("spi result was NULL"); + let b = Array::::new_from_slice(&[1, 2, 3, 4, 5]).expect("failed to create array"); + + assert_eq!(a.as_slice()?, b.as_slice()?); + + let a = Spi::get_one::>("SELECT ARRAY[1.0, 2.0, 3.0, 4.0, 5.0]::float4[]")? + .expect("spi result was NULL"); + let b = Array::::new_from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0]) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, b.as_slice()?); + + let a = Spi::get_one::>("SELECT ARRAY[1.0, 2.0, 3.0, 4.0, 5.0]::float8[]")? + .expect("spi result was NULL"); + let b = Array::::new_from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0]) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, b.as_slice()?); + + Ok(()) + } + + #[pg_test] + fn test_new_array_with_len() -> Result<(), Box> { + let a = Array::::new_with_len(5).expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[0, 0, 0, 0, 0]); + + let a = Array::::new_with_len(5).expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[0, 0, 0, 0, 0]); + + let a = Array::::new_with_len(5).expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[0, 0, 0, 0, 0]); + + let a = Array::::new_with_len(5).expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[0, 0, 0, 0, 0]); + + let a = Array::::new_with_len(5).expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[0.0, 0.0, 0.0, 0.0, 0.0]); + + let a = Array::::new_with_len(5).expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[0.0, 0.0, 0.0, 0.0, 0.0]); + + Ok(()) + } } diff --git a/pgrx/src/array.rs b/pgrx/src/array.rs index 7e4eeec807..f2771cbbd3 100644 --- a/pgrx/src/array.rs +++ b/pgrx/src/array.rs @@ -8,13 +8,14 @@ //LICENSE //LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file. #![allow(clippy::precedence)] -use crate::datum::Array; +use crate::datum::{Array, IntoDatum, UnboxDatum}; use crate::toast::{Toast, Toasty}; -use crate::{layout, pg_sys, varlena}; +use crate::{layout, pg_sys, set_varsize_4b, varlena, PgMemoryContexts}; use bitvec::ptr::{self as bitptr, BitPtr, BitPtrError, Mut}; use bitvec::slice::BitSlice; use core::ptr::{self, NonNull}; use core::slice; +use pgrx_pg_sys::ArrayType; mod port; @@ -373,6 +374,55 @@ impl RawArray { let ptr = self.ptr.as_ptr().cast::(); ptr.wrapping_add(unsafe { varlena::varsize_any(ptr.cast()) }) } + + /// Slightly faster than new_array_type_with_len(0) + pub fn new_empty_array_type() -> Result + where + T: IntoDatum, + T: UnboxDatum, + T: Sized, + { + unsafe { + let array_type = pg_sys::construct_empty_array(T::type_oid()); + let array_type = + NonNull::new(array_type).ok_or(ArrayAllocError::MemoryAllocationFailed)?; + Ok(RawArray::from_ptr(array_type)) + } + } + + /// Rustified version of new_intArrayType(int num) from https://github.com/postgres/postgres/blob/master/contrib/intarray/_int_tool.c#L219 + pub fn new_array_type_with_len(len: usize) -> Result + where + T: IntoDatum, + T: UnboxDatum, + T: Sized, + { + if len == 0 { + return Self::new_empty_array_type::(); + } + let elem_size = std::mem::size_of::(); + let nbytes: usize = port::ARR_OVERHEAD_NONULLS(1) + elem_size * len; + + unsafe { + let array_type = PgMemoryContexts::For(pg_sys::CurrentMemoryContext).palloc0(nbytes) + as *mut ArrayType; + if array_type.is_null() { + return Err(ArrayAllocError::MemoryAllocationFailed); + } + set_varsize_4b(array_type as *mut pg_sys::varlena, nbytes as i32); + (*array_type).ndim = 1; + (*array_type).dataoffset = 0; /* marker for no null bitmap */ + (*array_type).elemtype = T::type_oid(); + + let ndims = port::ARR_DIMS(array_type); + *ndims = len as i32; // equivalent of ARR_DIMS(r)[0] = num; + let arr_lbound = port::ARR_LBOUND(array_type); + *arr_lbound = 1; + + let array_type = NonNull::new_unchecked(array_type); + Ok(RawArray::from_ptr(array_type)) + } + } } impl Toasty for RawArray { @@ -380,3 +430,9 @@ impl Toasty for RawArray { unsafe { pg_sys::pfree(self.ptr.as_ptr().cast()) } } } + +#[derive(thiserror::Error, Debug, Copy, Clone, Eq, PartialEq)] +pub enum ArrayAllocError { + #[error("Failed to allocate memory for Array")] + MemoryAllocationFailed, +} diff --git a/pgrx/src/array/port.rs b/pgrx/src/array/port.rs index 25ca90e7b6..b31efc2623 100644 --- a/pgrx/src/array/port.rs +++ b/pgrx/src/array/port.rs @@ -126,3 +126,22 @@ pub(super) unsafe fn ARR_DATA_PTR(a: *mut pg_sys::ArrayType) -> *mut u8 { unsafe { a.cast::().add(ARR_DATA_OFFSET(a)) } } + +/// Returns a pointer to the lower bounds of the array. +/// # Safety +/// Does a field access, but doesn't deref out of bounds of ArrayType. The caller asserts that +/// `a` is a properly allocated [`pg_sys::ArrayType`] +/// +/// [`pg_sys::ArrayType`] is typically allocated past its size, and its somewhere in that region +/// that the returned pointer points, so don't attempt to `pfree` it. +#[inline(always)] +pub(super) unsafe fn ARR_LBOUND(a: *mut pg_sys::ArrayType) -> *mut i32 { + // #define ARR_LBOUND(a) \ + // ((int *) (((char *) (a)) + sizeof(ArrayType) + \ + // sizeof(int) * ARR_NDIM(a))) + + a.cast::() + .add(std::mem::size_of::()) + .add(std::mem::size_of::() * ((*a).ndim as usize)) + .cast::() +} diff --git a/pgrx/src/callconv.rs b/pgrx/src/callconv.rs index 8bc31d06db..7a2e1e80bf 100644 --- a/pgrx/src/callconv.rs +++ b/pgrx/src/callconv.rs @@ -20,9 +20,9 @@ use crate::datum::{Range, RangeSubType}; use crate::heap_tuple::PgHeapTuple; use crate::layout::PassBy; use crate::nullable::Nullable; -use crate::pg_sys; use crate::pgbox::*; use crate::rel::PgRelation; +use crate::{pg_sys, Array}; use crate::{PgBox, PgMemoryContexts}; use core::marker::PhantomData; @@ -600,6 +600,19 @@ where } } +unsafe impl<'mcx, T: UnboxDatum> BoxRet for Array<'mcx, T> +where + T: IntoDatum, +{ + #[inline] + unsafe fn box_into<'fcx>(self, fcinfo: &mut FcInfo<'fcx>) -> Datum<'fcx> { + match self.into_datum() { + Some(datum) => unsafe { fcinfo.return_raw_datum(datum) }, + None => fcinfo.return_null(), + } + } +} + unsafe impl BoxRet for PgVarlena { unsafe fn box_into<'fcx>(self, fcinfo: &mut FcInfo<'fcx>) -> Datum<'fcx> { match self.into_datum() { diff --git a/pgrx/src/datum/array.rs b/pgrx/src/datum/array.rs index 6659526de3..f980cc184f 100644 --- a/pgrx/src/datum/array.rs +++ b/pgrx/src/datum/array.rs @@ -9,7 +9,7 @@ //LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file. #![allow(clippy::question_mark)] use super::{unbox, UnboxDatum}; -use crate::array::RawArray; +use crate::array::{ArrayAllocError, RawArray}; use crate::nullable::{ BitSliceNulls, IntoNullableIterator, MaybeStrictNulls, NullLayout, Nullable, NullableContainer, }; @@ -424,6 +424,31 @@ impl Array<'_, f64> { pub fn as_slice(&self) -> Result<&[f64], ArraySliceError> { as_slice(self) } + + /// Returns a mutable slice of `f64`s which comprise this [`Array`]. + /// + /// # Errors + /// + /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more + /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. + #[inline] + pub fn as_mut_slice(&mut self) -> Result<&mut [f64], ArraySliceError> { + as_mut_slice(self) + } +} + +impl<'mcx> Array<'mcx, f64> { + #[inline] + pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { + new_array_with_len(len) + } + + #[inline(always)] + pub fn new_from_slice(slice: &'_ [f64]) -> Result, ArrayAllocError> { + let mut array = Self::new_with_len(slice.len())?; + array.as_mut_slice().unwrap().copy_from_slice(slice); + Ok(array) + } } impl Array<'_, f32> { @@ -437,6 +462,31 @@ impl Array<'_, f32> { pub fn as_slice(&self) -> Result<&[f32], ArraySliceError> { as_slice(self) } + + /// Returns a mutable slice of `f32`s which comprise this [`Array`]. + /// + /// # Errors + /// + /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more + /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. + #[inline] + pub fn as_mut_slice(&mut self) -> Result<&mut [f32], ArraySliceError> { + as_mut_slice(self) + } +} + +impl<'mcx> Array<'mcx, f32> { + #[inline] + pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { + new_array_with_len(len) + } + + #[inline(always)] + pub fn new_from_slice(slice: &'_ [f32]) -> Result, ArrayAllocError> { + let mut array = Self::new_with_len(slice.len())?; + array.as_mut_slice().unwrap().copy_from_slice(slice); + Ok(array) + } } #[cfg(target_pointer_width = "64")] @@ -451,6 +501,31 @@ impl Array<'_, i64> { pub fn as_slice(&self) -> Result<&[i64], ArraySliceError> { as_slice(self) } + + /// Returns a mutable slice of `i64`s which comprise this [`Array`]. + /// + /// # Errors + /// + /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more + /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. + #[inline] + pub fn as_mut_slice(&mut self) -> Result<&mut [i64], ArraySliceError> { + as_mut_slice(self) + } +} + +impl<'mcx> Array<'mcx, i64> { + #[inline] + pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { + new_array_with_len(len) + } + + #[inline(always)] + pub fn new_from_slice(slice: &'_ [i64]) -> Result, ArrayAllocError> { + let mut array = Self::new_with_len(slice.len())?; + array.as_mut_slice().unwrap().copy_from_slice(slice); + Ok(array) + } } impl Array<'_, i32> { @@ -464,6 +539,31 @@ impl Array<'_, i32> { pub fn as_slice(&self) -> Result<&[i32], ArraySliceError> { as_slice(self) } + + /// Returns a mutable slice of `i32`s which comprise this [`Array`]. + /// + /// # Errors + /// + /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more + /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. + #[inline] + pub fn as_mut_slice(&mut self) -> Result<&mut [i32], ArraySliceError> { + as_mut_slice(self) + } +} + +impl<'mcx> Array<'mcx, i32> { + #[inline] + pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { + new_array_with_len(len) + } + + #[inline(always)] + pub fn new_from_slice(slice: &'_ [i32]) -> Result, ArrayAllocError> { + let mut array = Self::new_with_len(slice.len())?; + array.as_mut_slice().unwrap().copy_from_slice(slice); + Ok(array) + } } impl Array<'_, i16> { @@ -477,6 +577,31 @@ impl Array<'_, i16> { pub fn as_slice(&self) -> Result<&[i16], ArraySliceError> { as_slice(self) } + + /// Returns a mutable slice of `i16`s which comprise this [`Array`]. + /// + /// # Errors + /// + /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more + /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. + #[inline] + pub fn as_mut_slice(&mut self) -> Result<&mut [i16], ArraySliceError> { + as_mut_slice(self) + } +} + +impl<'mcx> Array<'mcx, i16> { + #[inline] + pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { + new_array_with_len(len) + } + + #[inline(always)] + pub fn new_from_slice(slice: &'_ [i16]) -> Result, ArrayAllocError> { + let mut array = Self::new_with_len(slice.len())?; + array.as_mut_slice().unwrap().copy_from_slice(slice); + Ok(array) + } } impl Array<'_, i8> { @@ -490,6 +615,31 @@ impl Array<'_, i8> { pub fn as_slice(&self) -> Result<&[i8], ArraySliceError> { as_slice(self) } + + /// Returns a mutable slice of `i8`s which comprise this [`Array`]. + /// + /// # Errors + /// + /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more + /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. + #[inline] + pub fn as_mut_slice(&mut self) -> Result<&mut [i8], ArraySliceError> { + as_mut_slice(self) + } +} + +impl<'mcx> Array<'mcx, i8> { + #[inline] + pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { + new_array_with_len(len) + } + + #[inline(always)] + pub fn new_from_slice(slice: &'_ [i8]) -> Result, ArrayAllocError> { + let mut array = Self::new_with_len(slice.len())?; + array.as_mut_slice().unwrap().copy_from_slice(slice); + Ok(array) + } } #[inline(always)] @@ -503,6 +653,52 @@ fn as_slice<'a, T: Sized>(array: &'a Array<'_, T>) -> Result<&'a [T], ArraySlice Ok(slice) } +#[inline(always)] +fn as_mut_slice<'a, T: Sized>(array: &'a mut Array<'_, T>) -> Result<&'a mut [T], ArraySliceError> { + if array.contains_nulls() { + return Err(ArraySliceError::ContainsNulls); + } + + let slice = + unsafe { std::slice::from_raw_parts_mut(array.raw.data_ptr() as *mut _, array.len()) }; + Ok(slice) +} + +/// Creates an Array<`a, T> with zero-elements +/// Slightly faster than new_array_with_len(0) +pub fn new_empty_array<'a, T: Sized>() -> Result, ArrayAllocError> +where + T: IntoDatum, + T: UnboxDatum, +{ + unsafe { + let raw_array = RawArray::new_empty_array_type::()?; + let datum: pgrx_pg_sys::Datum = pg_sys::Datum::from(raw_array.into_ptr().as_ptr()); + Array::<'a, T>::from_polymorphic_datum(datum, false, pg_sys::get_array_type(T::type_oid())) + .ok_or(ArrayAllocError::MemoryAllocationFailed) + } +} + +/// Creates an Array of a fixed len, with 0 for all elements +/// Uses a single PG allocation rather than +#[inline(always)] +pub fn new_array_with_len<'a, T: Sized>(len: usize) -> Result, ArrayAllocError> +where + T: IntoDatum, + T: UnboxDatum, +{ + if len == 0 { + return new_empty_array(); + } + + let raw_array = RawArray::new_array_type_with_len::(len)?; + let datum: pgrx_pg_sys::Datum = pg_sys::Datum::from(raw_array.into_ptr().as_ptr()); + unsafe { + Array::<'a, T>::from_polymorphic_datum(datum, false, pg_sys::get_array_type(T::type_oid())) + .ok_or(ArrayAllocError::MemoryAllocationFailed) + } +} + mod casper { use super::UnboxDatum; use crate::layout::Align; @@ -946,7 +1142,7 @@ impl IntoDatum for Array<'_, T> { #[inline] fn into_datum(self) -> Option { let array_type = self.into_array_type(); - let datum = pg_sys::Datum::from(array_type); + let datum: pgrx_pg_sys::Datum = pg_sys::Datum::from(array_type); Some(datum) } From 14ef1b8721cb4749c4710cd4dc32a4a9d9bb61a0 Mon Sep 17 00:00:00 2001 From: mhov Date: Tue, 24 Jun 2025 16:57:44 -0700 Subject: [PATCH 2/7] fixed documention generation --- pgrx/src/datum/array.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pgrx/src/datum/array.rs b/pgrx/src/datum/array.rs index f980cc184f..bbf503eb19 100644 --- a/pgrx/src/datum/array.rs +++ b/pgrx/src/datum/array.rs @@ -664,7 +664,7 @@ fn as_mut_slice<'a, T: Sized>(array: &'a mut Array<'_, T>) -> Result<&'a mut [T] Ok(slice) } -/// Creates an Array<`a, T> with zero-elements +/// Creates an `Array<'a, T>` with zero-elements /// Slightly faster than new_array_with_len(0) pub fn new_empty_array<'a, T: Sized>() -> Result, ArrayAllocError> where @@ -679,7 +679,7 @@ where } } -/// Creates an Array of a fixed len, with 0 for all elements +/// Creates an `Array` of a fixed len, with 0 for all elements /// Uses a single PG allocation rather than #[inline(always)] pub fn new_array_with_len<'a, T: Sized>(len: usize) -> Result, ArrayAllocError> From 29eafb89355eb7632bad359ed0042aabecdd850d Mon Sep 17 00:00:00 2001 From: mhov Date: Fri, 12 Sep 2025 18:05:59 -0700 Subject: [PATCH 3/7] adding ArrayFastAllocSubType trait and typos --- pgrx/src/array.rs | 10 +++------- pgrx/src/array/port.rs | 6 +++--- pgrx/src/datum/array.rs | 28 +++++++++++++++++++++------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/pgrx/src/array.rs b/pgrx/src/array.rs index f2771cbbd3..0e2dec1963 100644 --- a/pgrx/src/array.rs +++ b/pgrx/src/array.rs @@ -8,7 +8,7 @@ //LICENSE //LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file. #![allow(clippy::precedence)] -use crate::datum::{Array, IntoDatum, UnboxDatum}; +use crate::datum::{Array, ArrayFastAllocSubType}; use crate::toast::{Toast, Toasty}; use crate::{layout, pg_sys, set_varsize_4b, varlena, PgMemoryContexts}; use bitvec::ptr::{self as bitptr, BitPtr, BitPtrError, Mut}; @@ -378,9 +378,7 @@ impl RawArray { /// Slightly faster than new_array_type_with_len(0) pub fn new_empty_array_type() -> Result where - T: IntoDatum, - T: UnboxDatum, - T: Sized, + T: ArrayFastAllocSubType, { unsafe { let array_type = pg_sys::construct_empty_array(T::type_oid()); @@ -393,9 +391,7 @@ impl RawArray { /// Rustified version of new_intArrayType(int num) from https://github.com/postgres/postgres/blob/master/contrib/intarray/_int_tool.c#L219 pub fn new_array_type_with_len(len: usize) -> Result where - T: IntoDatum, - T: UnboxDatum, - T: Sized, + T: ArrayFastAllocSubType, { if len == 0 { return Self::new_empty_array_type::(); diff --git a/pgrx/src/array/port.rs b/pgrx/src/array/port.rs index b31efc2623..94d363ce07 100644 --- a/pgrx/src/array/port.rs +++ b/pgrx/src/array/port.rs @@ -127,7 +127,7 @@ pub(super) unsafe fn ARR_DATA_PTR(a: *mut pg_sys::ArrayType) -> *mut u8 { unsafe { a.cast::().add(ARR_DATA_OFFSET(a)) } } -/// Returns a pointer to the lower bounds of the array. +/// Returns a pointer to the list of lower bounds given by Postgres as a series of integers /// # Safety /// Does a field access, but doesn't deref out of bounds of ArrayType. The caller asserts that /// `a` is a properly allocated [`pg_sys::ArrayType`] @@ -141,7 +141,7 @@ pub(super) unsafe fn ARR_LBOUND(a: *mut pg_sys::ArrayType) -> *mut i32 { // sizeof(int) * ARR_NDIM(a))) a.cast::() - .add(std::mem::size_of::()) - .add(std::mem::size_of::() * ((*a).ndim as usize)) + .add(mem::size_of::()) + .add(mem::size_of::() * ((*a).ndim as usize)) .cast::() } diff --git a/pgrx/src/datum/array.rs b/pgrx/src/datum/array.rs index bbf503eb19..e057d3e993 100644 --- a/pgrx/src/datum/array.rs +++ b/pgrx/src/datum/array.rs @@ -666,10 +666,9 @@ fn as_mut_slice<'a, T: Sized>(array: &'a mut Array<'_, T>) -> Result<&'a mut [T] /// Creates an `Array<'a, T>` with zero-elements /// Slightly faster than new_array_with_len(0) -pub fn new_empty_array<'a, T: Sized>() -> Result, ArrayAllocError> +pub fn new_empty_array<'a, T>() -> Result, ArrayAllocError> where - T: IntoDatum, - T: UnboxDatum, + T: ArrayFastAllocSubType, { unsafe { let raw_array = RawArray::new_empty_array_type::()?; @@ -680,12 +679,11 @@ where } /// Creates an `Array` of a fixed len, with 0 for all elements -/// Uses a single PG allocation rather than +/// Uses a single PG allocation rather than pg_sys::accumArrayResult(..) #[inline(always)] -pub fn new_array_with_len<'a, T: Sized>(len: usize) -> Result, ArrayAllocError> +pub fn new_array_with_len<'a, T>(len: usize) -> Result, ArrayAllocError> where - T: IntoDatum, - T: UnboxDatum, + T: ArrayFastAllocSubType, { if len == 0 { return new_empty_array(); @@ -1350,3 +1348,19 @@ where true } } + +/// This trait allows for arrays of certain numeric types to use Array's single allocation strategy +pub trait ArrayFastAllocSubType: Sized + UnboxDatum + IntoDatum {} + +// for char +impl ArrayFastAllocSubType for i8 {} +// for smallint +impl ArrayFastAllocSubType for i16 {} +// for integer +impl ArrayFastAllocSubType for i32 {} +// for bigint +impl ArrayFastAllocSubType for i64 {} +// for real +impl ArrayFastAllocSubType for f32 {} +// for double precision +impl ArrayFastAllocSubType for f64 {} From 1e650f586d014141973c16b63dba4e71020fb7c6 Mon Sep 17 00:00:00 2001 From: mhov Date: Fri, 12 Sep 2025 22:21:50 -0700 Subject: [PATCH 4/7] rustdoc corrections --- pgrx/src/array.rs | 2 +- pgrx/src/datum/array.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pgrx/src/array.rs b/pgrx/src/array.rs index 0e2dec1963..da93a90ebc 100644 --- a/pgrx/src/array.rs +++ b/pgrx/src/array.rs @@ -388,7 +388,7 @@ impl RawArray { } } - /// Rustified version of new_intArrayType(int num) from https://github.com/postgres/postgres/blob/master/contrib/intarray/_int_tool.c#L219 + /// Rustified version of new_intArrayType(int num) from [https://github.com/postgres/postgres/blob/master/contrib/intarray/_int_tool.c#L219] pub fn new_array_type_with_len(len: usize) -> Result where T: ArrayFastAllocSubType, diff --git a/pgrx/src/datum/array.rs b/pgrx/src/datum/array.rs index e057d3e993..91ec033a1d 100644 --- a/pgrx/src/datum/array.rs +++ b/pgrx/src/datum/array.rs @@ -1349,7 +1349,7 @@ where } } -/// This trait allows for arrays of certain numeric types to use Array's single allocation strategy +/// This trait allows for arrays of certain numeric types to use Array<T>'s single allocation strategy pub trait ArrayFastAllocSubType: Sized + UnboxDatum + IntoDatum {} // for char From d0b522ecee3eb58b774db273e4e78ff568a32a8d Mon Sep 17 00:00:00 2001 From: mhov Date: Wed, 15 Oct 2025 19:23:13 -0700 Subject: [PATCH 5/7] Update pgrx/src/datum/array.rs Co-authored-by: Jubilee --- pgrx/src/datum/array.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgrx/src/datum/array.rs b/pgrx/src/datum/array.rs index 91ec033a1d..fe0a79dfb3 100644 --- a/pgrx/src/datum/array.rs +++ b/pgrx/src/datum/array.rs @@ -1349,7 +1349,7 @@ where } } -/// This trait allows for arrays of certain numeric types to use Array<T>'s single allocation strategy +/// This trait allows for arrays of certain numeric types to use `Array`'s single allocation strategy pub trait ArrayFastAllocSubType: Sized + UnboxDatum + IntoDatum {} // for char From 7c578fae228089f52cbd2e499fe531f0758277a9 Mon Sep 17 00:00:00 2001 From: mhov Date: Wed, 29 Oct 2025 22:26:50 -0700 Subject: [PATCH 6/7] Array new_from_iter and new_with_init_fn constructors --- pgrx-tests/src/tests/array_tests.rs | 106 +++++++ pgrx/src/array.rs | 2 + pgrx/src/datum/array.rs | 450 +++++++++++----------------- 3 files changed, 275 insertions(+), 283 deletions(-) diff --git a/pgrx-tests/src/tests/array_tests.rs b/pgrx-tests/src/tests/array_tests.rs index ad18db2f0a..e4e078b8a7 100644 --- a/pgrx-tests/src/tests/array_tests.rs +++ b/pgrx-tests/src/tests/array_tests.rs @@ -195,6 +195,7 @@ mod tests { use crate as pgrx_tests; use super::ArrayTestEnum; + use pgrx::array::ArrayAllocError; use pgrx::prelude::*; use pgrx::Json; use serde_json::json; @@ -592,4 +593,109 @@ mod tests { Ok(()) } + + #[pg_test] + fn test_new_array_from_iter() -> Result<(), Box> { + let a = Array::::new_from_iter(vec![1, 2, 3, 4, 5].into_iter(), 5) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[1, 2, 3, 4, 5]); + + let a = Array::::new_from_iter(vec![1, 2, 3, 4, 5].into_iter(), 5) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[1, 2, 3, 4, 5]); + + let a = Array::::new_from_iter(vec![1, 2, 3, 4, 5].into_iter(), 5) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[1, 2, 3, 4, 5]); + + let a = Array::::new_from_iter(vec![1, 2, 3, 4, 5].into_iter(), 5) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[1, 2, 3, 4, 5]); + + let a = Array::::new_from_iter(vec![1.0, 2.0, 3.0, 4.0, 5.0].into_iter(), 5) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[1.0, 2.0, 3.0, 4.0, 5.0]); + + let a = Array::::new_from_iter(vec![1.0, 2.0, 3.0, 4.0, 5.0].into_iter(), 5) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[1.0, 2.0, 3.0, 4.0, 5.0]); + + // a more complex iter + let iter = vec![1i32, 2, 3].into_iter().chain(vec![4i32, 5].into_iter()); + let a = Array::::new_from_iter(iter, 5).expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[1, 2, 3, 4, 5]); + + // make sure the expected allocated len matches the enumerated len + let a = Array::::new_from_iter(vec![1, 2, 3, 4, 5].into_iter(), 3); + + assert_eq!(a.err(), Some(ArrayAllocError::IterLenMismatch(3, 5))); // expected 3, saw 5 + + Ok(()) + } + + #[pg_test] + fn test_new_array_with_init_fn() -> Result<(), Box> { + let a = Array::::new_with_init_fn(5, |slice| { + for (i, elem) in slice.iter_mut().enumerate() { + *elem = (i + 1) as i8; + } + }) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[1, 2, 3, 4, 5]); + + let a = Array::::new_with_init_fn(5, |slice| { + for (i, elem) in slice.iter_mut().enumerate() { + *elem = (i + 1) as i16; + } + }) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[1, 2, 3, 4, 5]); + + let a = Array::::new_with_init_fn(5, |slice| { + for (i, elem) in slice.iter_mut().enumerate() { + *elem = (i + 1) as i32; + } + }) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[1, 2, 3, 4, 5]); + + let a = Array::::new_with_init_fn(5, |slice| { + for (i, elem) in slice.iter_mut().enumerate() { + *elem = (i + 1) as i64; + } + }) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[1, 2, 3, 4, 5]); + + let a = Array::::new_with_init_fn(5, |slice| { + for (i, elem) in slice.iter_mut().enumerate() { + *elem = (i + 1) as f32; + } + }) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[1.0, 2.0, 3.0, 4.0, 5.0]); + + let a = Array::::new_with_init_fn(5, |slice| { + for (i, elem) in slice.iter_mut().enumerate() { + *elem = (i + 1) as f64; + } + }) + .expect("failed to create array"); + + assert_eq!(a.as_slice()?, &[1.0, 2.0, 3.0, 4.0, 5.0]); + + Ok(()) + } } diff --git a/pgrx/src/array.rs b/pgrx/src/array.rs index da93a90ebc..b5c7a99da9 100644 --- a/pgrx/src/array.rs +++ b/pgrx/src/array.rs @@ -431,4 +431,6 @@ impl Toasty for RawArray { pub enum ArrayAllocError { #[error("Failed to allocate memory for Array")] MemoryAllocationFailed, + #[error("Expected len {0}, found {1}")] + IterLenMismatch(usize, usize), } diff --git a/pgrx/src/datum/array.rs b/pgrx/src/datum/array.rs index fe0a79dfb3..ae6c6980fc 100644 --- a/pgrx/src/datum/array.rs +++ b/pgrx/src/datum/array.rs @@ -351,6 +351,137 @@ impl Array<'_, T> { } } +impl<'mcx, T> Array<'mcx, T> +where + T: ArrayFastAllocSubType, +{ + /// Returns a mutable slice of `T`s which comprise this [`Array`]. + /// + /// # Errors + /// + /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more + /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. + /// + /// # Safety + /// Use internally only for array construction, + /// We don't want to pass around a mutable reference to array data which we didn't specifically create + #[inline(always)] + pub(crate) fn as_mut_slice(&mut self) -> Result<&mut [T], ArraySliceError> { + if self.contains_nulls() { + return Err(ArraySliceError::ContainsNulls); + } + let slice = + unsafe { std::slice::from_raw_parts_mut(self.raw.data_ptr() as *mut _, self.len()) }; + Ok(slice) + } + + // Returns a slice of `T`s which comprise this [`Array`]. + /// + /// # Errors + /// + /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more + /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. + #[inline] + pub fn as_slice(&self) -> Result<&[T], ArraySliceError> { + if self.contains_nulls() { + return Err(ArraySliceError::ContainsNulls); + } + + let slice = + unsafe { std::slice::from_raw_parts(self.raw.data_ptr() as *const _, self.len()) }; + Ok(slice) + } + + /// Creates an `Array<'a, T>` with zero-elements + /// Slightly faster than new_array_with_len(0) + pub fn new_empty_array<'a>() -> Result, ArrayAllocError> + where + T: ArrayFastAllocSubType, + { + unsafe { + let raw_array = RawArray::new_empty_array_type::()?; + let datum: pgrx_pg_sys::Datum = pg_sys::Datum::from(raw_array.into_ptr().as_ptr()); + Array::<'a, T>::from_polymorphic_datum( + datum, + false, + pg_sys::get_array_type(T::type_oid()), + ) + .ok_or(ArrayAllocError::MemoryAllocationFailed) + } + } + + /// Creates an `Array<'a, T>` of a fixed len, with 0 for all elements + /// Uses a single PG allocation rather than pg_sys::accumArrayResult(..) + pub fn new_with_len<'a>(len: usize) -> Result, ArrayAllocError> + where + T: ArrayFastAllocSubType, + { + if len == 0 { + return Self::new_empty_array(); + } + + let raw_array = RawArray::new_array_type_with_len::(len)?; + let datum: pgrx_pg_sys::Datum = pg_sys::Datum::from(raw_array.into_ptr().as_ptr()); + unsafe { + Array::<'a, T>::from_polymorphic_datum( + datum, + false, + pg_sys::get_array_type(T::type_oid()), + ) + .ok_or(ArrayAllocError::MemoryAllocationFailed) + } + } + + /// Constructs an `Array<'a, T>` with a single allocation and enumerating the iter + /// + /// iter_len must equal the total length of the iter. You must know the iter_len ahead of time to prevent multiple enumerations/allocations + /// + /// # Errors + /// Returns a [`ArrayAllocError::IterLenMismatch`] iter's total len != iter_len + pub fn new_from_iter<'a, I>(iter: I, iter_len: usize) -> Result, ArrayAllocError> + where + T: ArrayFastAllocSubType, + I: Iterator, + { + let mut array = Self::new_with_len(iter_len)?; + let slice = array.as_mut_slice().unwrap(); + let mut count = 0; + for (idx, item) in iter.enumerate() { + if idx < iter_len { + slice[idx] = item; + } + count += 1; + } + if count != iter_len { + return Err(ArrayAllocError::IterLenMismatch(iter_len, count)); + } + Ok(array) + } + + /// Constructs an `Array<'a, T>` with a single allocation and copy_from_slice + pub fn new_from_slice<'a>(slice: &[T]) -> Result, ArrayAllocError> + where + T: ArrayFastAllocSubType, + { + let mut array = Self::new_with_len(slice.len())?; + let array_slice = array.as_mut_slice().unwrap(); + array_slice.copy_from_slice(slice); + Ok(array) + } + + /// Constructs an `Array<'a, T>` with a single allocation and running init_fn on the Array's `&mut [T]` + pub fn new_with_init_fn<'a, F>(len: usize, init_fn: F) -> Result, ArrayAllocError> + where + T: ArrayFastAllocSubType, + F: FnOnce(&mut [T]) -> (), + { + let mut array = Self::new_with_len(len)?; + let slice = array.as_mut_slice().unwrap(); + init_fn(slice); + Ok(array) + } +} + /// Adapter to use `Nullable` for array iteration. pub struct NullableArrayIterator<'mcx, T> where @@ -412,289 +543,40 @@ pub enum ArraySliceError { ContainsNulls, } -#[cfg(target_pointer_width = "64")] -impl Array<'_, f64> { - /// Returns a slice of `f64`s which comprise this [`Array`]. - /// - /// # Errors - /// - /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more - /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. - #[inline] - pub fn as_slice(&self) -> Result<&[f64], ArraySliceError> { - as_slice(self) - } - - /// Returns a mutable slice of `f64`s which comprise this [`Array`]. - /// - /// # Errors - /// - /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more - /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. - #[inline] - pub fn as_mut_slice(&mut self) -> Result<&mut [f64], ArraySliceError> { - as_mut_slice(self) - } -} - -impl<'mcx> Array<'mcx, f64> { - #[inline] - pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { - new_array_with_len(len) - } - - #[inline(always)] - pub fn new_from_slice(slice: &'_ [f64]) -> Result, ArrayAllocError> { - let mut array = Self::new_with_len(slice.len())?; - array.as_mut_slice().unwrap().copy_from_slice(slice); - Ok(array) - } -} - -impl Array<'_, f32> { - /// Returns a slice of `f32`s which comprise this [`Array`]. - /// - /// # Errors - /// - /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more - /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. - #[inline] - pub fn as_slice(&self) -> Result<&[f32], ArraySliceError> { - as_slice(self) - } - - /// Returns a mutable slice of `f32`s which comprise this [`Array`]. - /// - /// # Errors - /// - /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more - /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. - #[inline] - pub fn as_mut_slice(&mut self) -> Result<&mut [f32], ArraySliceError> { - as_mut_slice(self) - } -} - -impl<'mcx> Array<'mcx, f32> { - #[inline] - pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { - new_array_with_len(len) - } - - #[inline(always)] - pub fn new_from_slice(slice: &'_ [f32]) -> Result, ArrayAllocError> { - let mut array = Self::new_with_len(slice.len())?; - array.as_mut_slice().unwrap().copy_from_slice(slice); - Ok(array) - } -} - -#[cfg(target_pointer_width = "64")] -impl Array<'_, i64> { - /// Returns a slice of `i64`s which comprise this [`Array`]. - /// - /// # Errors - /// - /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more - /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. - #[inline] - pub fn as_slice(&self) -> Result<&[i64], ArraySliceError> { - as_slice(self) - } - - /// Returns a mutable slice of `i64`s which comprise this [`Array`]. - /// - /// # Errors - /// - /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more - /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. - #[inline] - pub fn as_mut_slice(&mut self) -> Result<&mut [i64], ArraySliceError> { - as_mut_slice(self) - } -} - -impl<'mcx> Array<'mcx, i64> { - #[inline] - pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { - new_array_with_len(len) - } - - #[inline(always)] - pub fn new_from_slice(slice: &'_ [i64]) -> Result, ArrayAllocError> { - let mut array = Self::new_with_len(slice.len())?; - array.as_mut_slice().unwrap().copy_from_slice(slice); - Ok(array) - } -} - -impl Array<'_, i32> { - /// Returns a slice of `i32`s which comprise this [`Array`]. - /// - /// # Errors - /// - /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more - /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. - #[inline] - pub fn as_slice(&self) -> Result<&[i32], ArraySliceError> { - as_slice(self) - } - - /// Returns a mutable slice of `i32`s which comprise this [`Array`]. - /// - /// # Errors - /// - /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more - /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. - #[inline] - pub fn as_mut_slice(&mut self) -> Result<&mut [i32], ArraySliceError> { - as_mut_slice(self) - } -} - impl<'mcx> Array<'mcx, i32> { - #[inline] - pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { - new_array_with_len(len) - } - - #[inline(always)] - pub fn new_from_slice(slice: &'_ [i32]) -> Result, ArrayAllocError> { - let mut array = Self::new_with_len(slice.len())?; - array.as_mut_slice().unwrap().copy_from_slice(slice); - Ok(array) - } -} - -impl Array<'_, i16> { - /// Returns a slice of `i16`s which comprise this [`Array`]. - /// - /// # Errors - /// - /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more - /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. - #[inline] - pub fn as_slice(&self) -> Result<&[i16], ArraySliceError> { - as_slice(self) - } - - /// Returns a mutable slice of `i16`s which comprise this [`Array`]. - /// - /// # Errors - /// - /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more - /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. - #[inline] - pub fn as_mut_slice(&mut self) -> Result<&mut [i16], ArraySliceError> { - as_mut_slice(self) - } -} - -impl<'mcx> Array<'mcx, i16> { - #[inline] - pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { - new_array_with_len(len) - } - - #[inline(always)] - pub fn new_from_slice(slice: &'_ [i16]) -> Result, ArrayAllocError> { - let mut array = Self::new_with_len(slice.len())?; - array.as_mut_slice().unwrap().copy_from_slice(slice); - Ok(array) - } -} - -impl Array<'_, i8> { - /// Returns a slice of `i8`s which comprise this [`Array`]. - /// - /// # Errors - /// - /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more - /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. - #[inline] - pub fn as_slice(&self) -> Result<&[i8], ArraySliceError> { - as_slice(self) - } - - /// Returns a mutable slice of `i8`s which comprise this [`Array`]. - /// - /// # Errors - /// - /// Returns a [`ArraySliceError::ContainsNulls`] error if this [`Array`] contains one or more - /// SQL "NULL" values. In this case, you'd likely want to fallback to using [`Array::iter()`]. - #[inline] - pub fn as_mut_slice(&mut self) -> Result<&mut [i8], ArraySliceError> { - as_mut_slice(self) - } -} - -impl<'mcx> Array<'mcx, i8> { - #[inline] - pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { - new_array_with_len(len) - } - - #[inline(always)] - pub fn new_from_slice(slice: &'_ [i8]) -> Result, ArrayAllocError> { - let mut array = Self::new_with_len(slice.len())?; - array.as_mut_slice().unwrap().copy_from_slice(slice); - Ok(array) - } -} - -#[inline(always)] -fn as_slice<'a, T: Sized>(array: &'a Array<'_, T>) -> Result<&'a [T], ArraySliceError> { - if array.contains_nulls() { - return Err(ArraySliceError::ContainsNulls); - } - - let slice = - unsafe { std::slice::from_raw_parts(array.raw.data_ptr() as *const _, array.len()) }; - Ok(slice) -} - -#[inline(always)] -fn as_mut_slice<'a, T: Sized>(array: &'a mut Array<'_, T>) -> Result<&'a mut [T], ArraySliceError> { - if array.contains_nulls() { - return Err(ArraySliceError::ContainsNulls); - } - - let slice = - unsafe { std::slice::from_raw_parts_mut(array.raw.data_ptr() as *mut _, array.len()) }; - Ok(slice) -} - -/// Creates an `Array<'a, T>` with zero-elements -/// Slightly faster than new_array_with_len(0) -pub fn new_empty_array<'a, T>() -> Result, ArrayAllocError> -where - T: ArrayFastAllocSubType, -{ - unsafe { - let raw_array = RawArray::new_empty_array_type::()?; - let datum: pgrx_pg_sys::Datum = pg_sys::Datum::from(raw_array.into_ptr().as_ptr()); - Array::<'a, T>::from_polymorphic_datum(datum, false, pg_sys::get_array_type(T::type_oid())) - .ok_or(ArrayAllocError::MemoryAllocationFailed) - } -} - -/// Creates an `Array` of a fixed len, with 0 for all elements -/// Uses a single PG allocation rather than pg_sys::accumArrayResult(..) -#[inline(always)] -pub fn new_array_with_len<'a, T>(len: usize) -> Result, ArrayAllocError> -where - T: ArrayFastAllocSubType, -{ - if len == 0 { - return new_empty_array(); - } - - let raw_array = RawArray::new_array_type_with_len::(len)?; - let datum: pgrx_pg_sys::Datum = pg_sys::Datum::from(raw_array.into_ptr().as_ptr()); - unsafe { - Array::<'a, T>::from_polymorphic_datum(datum, false, pg_sys::get_array_type(T::type_oid())) - .ok_or(ArrayAllocError::MemoryAllocationFailed) - } + // #[inline] + // pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { + // new_array_with_len(len) + // } + + // #[inline(always)] + // pub fn new_from_slice(slice: &'_ [i32]) -> Result, ArrayAllocError> { + // let mut array = Self::new_with_len(slice.len())?; + // array.as_mut_slice().unwrap().copy_from_slice(slice); + // Ok(array) + // } + + // #[inline(always)] + // pub fn new_from_iter(iter: I, iter_len: usize) -> Result, ArrayAllocError> + // where + // I: Iterator, + // { + // Self::new_from_iter(iter, iter_len) + // let mut array = Self::new_with_len(iter_len)?; + // let slice = array.as_mut_slice().unwrap(); + // let mut count = 0; + // for (idx, item) in iter.enumerate() { + // if idx >= iter_len { + // return Err(ArrayAllocError::IterLenMismatch(iter_len, idx + iter.count())); + // } + // slice[idx] = item; + // count += 1; + // } + // if count != iter_len { + // return Err(ArrayAllocError::IterLenMismatch(iter_len, count)); + // } + // Ok(array) + // } } mod casper { @@ -1350,7 +1232,7 @@ where } /// This trait allows for arrays of certain numeric types to use `Array`'s single allocation strategy -pub trait ArrayFastAllocSubType: Sized + UnboxDatum + IntoDatum {} +pub trait ArrayFastAllocSubType: Sized + UnboxDatum + IntoDatum + Copy {} // for char impl ArrayFastAllocSubType for i8 {} @@ -1359,8 +1241,10 @@ impl ArrayFastAllocSubType for i16 {} // for integer impl ArrayFastAllocSubType for i32 {} // for bigint +#[cfg(target_pointer_width = "64")] impl ArrayFastAllocSubType for i64 {} // for real impl ArrayFastAllocSubType for f32 {} // for double precision +#[cfg(target_pointer_width = "64")] impl ArrayFastAllocSubType for f64 {} From 519157beffdf13782f7d13dd4491946827455931 Mon Sep 17 00:00:00 2001 From: mhov Date: Fri, 31 Oct 2025 15:43:00 -0700 Subject: [PATCH 7/7] Cleanup --- pgrx/src/datum/array.rs | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/pgrx/src/datum/array.rs b/pgrx/src/datum/array.rs index ae6c6980fc..c47e92ffaa 100644 --- a/pgrx/src/datum/array.rs +++ b/pgrx/src/datum/array.rs @@ -543,42 +543,6 @@ pub enum ArraySliceError { ContainsNulls, } -impl<'mcx> Array<'mcx, i32> { - // #[inline] - // pub fn new_with_len(len: usize) -> Result, ArrayAllocError> { - // new_array_with_len(len) - // } - - // #[inline(always)] - // pub fn new_from_slice(slice: &'_ [i32]) -> Result, ArrayAllocError> { - // let mut array = Self::new_with_len(slice.len())?; - // array.as_mut_slice().unwrap().copy_from_slice(slice); - // Ok(array) - // } - - // #[inline(always)] - // pub fn new_from_iter(iter: I, iter_len: usize) -> Result, ArrayAllocError> - // where - // I: Iterator, - // { - // Self::new_from_iter(iter, iter_len) - // let mut array = Self::new_with_len(iter_len)?; - // let slice = array.as_mut_slice().unwrap(); - // let mut count = 0; - // for (idx, item) in iter.enumerate() { - // if idx >= iter_len { - // return Err(ArrayAllocError::IterLenMismatch(iter_len, idx + iter.count())); - // } - // slice[idx] = item; - // count += 1; - // } - // if count != iter_len { - // return Err(ArrayAllocError::IterLenMismatch(iter_len, count)); - // } - // Ok(array) - // } -} - mod casper { use super::UnboxDatum; use crate::layout::Align;