#![allow(clippy::precedence)]
#![allow(unused)]
#![deny(unsafe_op_in_unsafe_fn)]
use crate::datum::{Array, BorrowDatum, Datum};
use crate::layout::{Align, Layout};
use crate::memcx::MemCx;
use crate::nullable::Nullable;
use crate::palloc::PBox;
use crate::pgrx_sql_entity_graph::metadata::{
ArgumentError, Returns, ReturnsError, SqlMapping, SqlTranslatable,
};
use crate::toast::{Toast, Toasty};
use crate::{layout, pg_sys, varlena};
use bitvec::ptr::{self as bitptr, BitPtr, BitPtrError, Const, Mut};
use bitvec::slice::{self as bitslice, BitSlice};
use core::iter::{ExactSizeIterator, FusedIterator};
use core::marker::PhantomData;
use core::ptr::{self, NonNull};
use core::{ffi, mem, slice};
mod element;
mod flat_array;
mod port;
pub use element::Element;
pub use flat_array::{ArrayAllocError, FlatArray};
#[derive(Debug)]
pub struct RawArray {
ptr: NonNull<pg_sys::ArrayType>,
}
#[deny(unsafe_op_in_unsafe_fn)]
impl RawArray {
pub unsafe fn from_ptr(ptr: NonNull<pg_sys::ArrayType>) -> RawArray {
RawArray { ptr }
}
pub(crate) unsafe fn detoast_from_varlena(stale: NonNull<pg_sys::varlena>) -> Toast<RawArray> {
unsafe {
let toast = NonNull::new(pg_sys::pg_detoast_datum(stale.as_ptr().cast())).unwrap();
if stale == toast {
Toast::Stale(RawArray::from_ptr(toast.cast()))
} else {
Toast::Fresh(RawArray::from_ptr(toast.cast()))
}
}
}
#[allow(dead_code)]
pub(crate) unsafe fn deconstruct(
&mut self,
layout: layout::Layout,
) -> (*mut pg_sys::Datum, *mut bool) {
let oid = self.oid();
let array = self.ptr.as_ptr();
let mut elements = core::ptr::null_mut();
let mut nulls = core::ptr::null_mut();
let mut nelems = 0;
unsafe {
pg_sys::deconstruct_array(
array,
oid,
layout.size.as_typlen().into(),
matches!(layout.pass, layout::PassBy::Value),
layout.align.as_typalign(),
&mut elements,
&mut nulls,
&mut nelems,
);
(elements, nulls)
}
}
pub unsafe fn from_array<T>(arr: Array<T>) -> Option<RawArray> {
let array_type = arr.into_array_type() as *mut _;
Some(RawArray { ptr: NonNull::new(array_type)? })
}
#[inline]
pub fn into_ptr(self) -> NonNull<pg_sys::ArrayType> {
self.ptr
}
#[inline]
fn ndim(&self) -> libc::c_int {
unsafe {
(*self.ptr.as_ptr()).ndim
as _
}
}
pub fn dims(&self) -> &[libc::c_int] {
unsafe {
let ndim = self.ndim() as usize;
slice::from_raw_parts(port::ARR_DIMS(self.ptr.as_ptr()), ndim)
}
}
#[inline]
pub fn len(&self) -> usize {
let dims = self.dims();
if dims.is_empty() {
0
} else {
const MAX_ARRAY_SIZE: u32 = 0x3fffffff / 8;
dims.iter()
.map(|i| *i as u32) .try_fold(1u32, |prod, d| prod.checked_mul(d))
.filter(|prod| prod <= &MAX_ARRAY_SIZE)
.expect("product of array dimensions must be < 2.pow(27)") as usize
}
}
#[inline]
pub fn oid(&self) -> pg_sys::Oid {
unsafe { (*self.ptr.as_ptr()).elemtype }
}
#[inline]
fn data_offset(&self) -> i32 {
unsafe { (*self.ptr.as_ptr()).dataoffset }
}
#[allow(unused)]
fn nullable(&self) -> bool {
self.data_offset() != 0
}
#[inline]
fn nulls_mut_ptr(&mut self) -> *mut u8 {
unsafe { port::ARR_NULLBITMAP(self.ptr.as_ptr()) }
}
#[inline]
fn nulls_bitptr(&self) -> Option<BitPtr<Const, u8>> {
let nulls_ptr = unsafe { port::ARR_NULLBITMAP(self.ptr.as_ptr()) }.cast_const();
match BitPtr::try_from(nulls_ptr) {
Ok(ptr) => Some(ptr),
Err(BitPtrError::Null(_)) => None,
Err(BitPtrError::Misaligned(_)) => unreachable!(),
}
}
#[inline]
fn nulls_mut_bitptr(&mut self) -> Option<BitPtr<Mut, u8>> {
let nulls_ptr = unsafe { port::ARR_NULLBITMAP(self.ptr.as_ptr()) };
match BitPtr::try_from(self.nulls_mut_ptr()) {
Ok(ptr) => Some(ptr),
Err(BitPtrError::Null(_)) => None,
Err(BitPtrError::Misaligned(_)) => unreachable!(),
}
}
pub fn nulls(&mut self) -> Option<NonNull<[u8]>> {
let len = self.len() + 7 >> 3;
NonNull::new(ptr::slice_from_raw_parts_mut(self.nulls_mut_ptr(), len))
}
pub fn nulls_bitslice(&mut self) -> Option<NonNull<BitSlice<u8>>> {
NonNull::new(bitptr::bitslice_from_raw_parts_mut(self.nulls_mut_bitptr()?, self.len()))
}
pub unsafe fn any_nulls(&self) -> bool {
unsafe { pg_sys::array_contains_nulls(self.ptr.as_ptr()) }
}
pub fn data<T>(&mut self) -> NonNull<[T]> {
unsafe {
NonNull::new_unchecked(ptr::slice_from_raw_parts_mut(
port::ARR_DATA_PTR(self.ptr.as_ptr()).cast(),
self.len(),
))
}
}
#[inline]
pub(crate) fn data_ptr(&self) -> *const u8 {
unsafe { port::ARR_DATA_PTR(self.ptr.as_ptr()) }
}
pub(crate) fn end_ptr(&self) -> *const u8 {
let ptr = self.ptr.as_ptr().cast::<u8>();
ptr.wrapping_add(unsafe { varlena::varsize_any(ptr.cast()) })
}
}
impl Toasty for RawArray {
unsafe fn drop_toast(&mut self) {
unsafe { pg_sys::pfree(self.ptr.as_ptr().cast()) }
}
}
pub unsafe trait Scalar: Sized + Copy + Element {
const OID: pg_sys::Oid;
}
unsafe impl Scalar for f32 {
const OID: pg_sys::Oid = pg_sys::FLOAT4OID;
}
#[cfg(target_pointer_width = "64")]
unsafe impl Scalar for f64 {
const OID: pg_sys::Oid = pg_sys::FLOAT8OID;
}
unsafe impl Scalar for i8 {
const OID: pg_sys::Oid = pg_sys::CHAROID;
}
unsafe impl Scalar for i16 {
const OID: pg_sys::Oid = pg_sys::INT2OID;
}
unsafe impl Scalar for i32 {
const OID: pg_sys::Oid = pg_sys::INT4OID;
}
unsafe impl Scalar for i64 {
const OID: pg_sys::Oid = pg_sys::INT8OID;
}