-
-
Notifications
You must be signed in to change notification settings - Fork 290
WIP: Adding support for contrib/intarray style fast-allocated arrays #2086
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,10 +374,65 @@ impl RawArray { | |
let ptr = self.ptr.as_ptr().cast::<u8>(); | ||
ptr.wrapping_add(unsafe { varlena::varsize_any(ptr.cast()) }) | ||
} | ||
|
||
/// Slightly faster than new_array_type_with_len(0) | ||
pub fn new_empty_array_type<T>() -> Result<RawArray, ArrayAllocError> | ||
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<T>(len: usize) -> Result<RawArray, ArrayAllocError> | ||
where | ||
T: IntoDatum, | ||
T: UnboxDatum, | ||
T: Sized, | ||
{ | ||
if len == 0 { | ||
return Self::new_empty_array_type::<T>(); | ||
} | ||
let elem_size = std::mem::size_of::<T>(); | ||
let nbytes: usize = port::ARR_OVERHEAD_NONULLS(1) + elem_size * len; | ||
|
||
unsafe { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In new code, a safe function with internal |
||
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 { | ||
unsafe fn drop_toast(&mut self) { | ||
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, | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -126,3 +126,22 @@ pub(super) unsafe fn ARR_DATA_PTR(a: *mut pg_sys::ArrayType) -> *mut u8 { | |||||||||
|
||||||||||
unsafe { a.cast::<u8>().add(ARR_DATA_OFFSET(a)) } | ||||||||||
} | ||||||||||
|
||||||||||
/// Returns a pointer to the lower bounds of the array. | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ...Maybe this should specify whether it actually points to the list of lower bounds given by Postgres as a series of integers, or the actual lower bound position in the actual data. ( The answer is the former. ) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For |
||||||||||
/// # 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::<u8>() | ||||||||||
.add(std::mem::size_of::<pg_sys::ArrayType>()) | ||||||||||
.add(std::mem::size_of::<i32>() * ((*a).ndim as usize)) | ||||||||||
Comment on lines
+144
to
+145
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style nit
Suggested change
|
||||||||||
.cast::<i32>() | ||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not okay to block something like:
It's better to mark this function as unsafe, and avoid exposing anything other than those specific
Array::new_with_len
methods.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hum. Could you elaborate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is
in the function body. This looks unreasonable.
IntoDatum + UnboxDatum + Sized
doesn't block types that is passed by value, and not all types of zero-initialization are valid.