pgrx/datum/
borrow.rs

1#![deny(unsafe_op_in_unsafe_fn)]
2use super::*;
3use crate::layout::PassBy;
4use core::{ffi, mem, ptr};
5
6/// Types which can be "borrowed from" [`&Datum<'_>`] via simple cast, deref, or slicing
7///
8/// # Safety
9/// Despite its pleasant-sounding name, this implements a fairly low-level detail.
10/// It exists to allow other code to use that nice-sounding BorrowDatum bound.
11/// Outside of the pgrx library, it is probably incorrect to call and rely on this:
12/// instead use convenience functions like `Datum::borrow_as`.
13///
14/// Its behavior is trusted for ABI details, and it should not be implemented if any doubt
15/// exists of whether the type would be suitable for passing via Postgres.
16pub unsafe trait BorrowDatum {
17    /// The "native" passing convention for this type.
18    ///
19    /// - `PassBy::Value` implies [`mem::size_of<T>()`][size_of] <= [`mem::size_of::<Datum>()`][Datum].
20    /// - `PassBy::Ref` means the pointee will occupy at least 1 byte for variable-sized types.
21    ///
22    /// Note that this means a zero-sized type is inappropriate for `BorrowDatum`.
23    const PASS: PassBy;
24
25    /// Cast a pointer to this blob of bytes to a pointer to this type.
26    ///
27    /// This is not a simple `ptr.cast()` because it may be *unsizing*, which may require
28    /// reading varlena headers. For all fixed-size types, `ptr.cast()` should be correct.
29    ///
30    /// # Safety
31    /// - This must be correctly invoked for the pointee type, as it may deref and read one or more
32    ///   bytes in its implementation in order to read the inline metadata and unsize the type.
33    /// - This must be invoked with a pointee initialized for the dynamically specified length.
34    ///
35    /// ## For Implementers
36    /// Reading the **first** byte pointed to is permitted if `T::PASS = PassBy::Ref`, assuming you
37    /// are implementing a varlena type. As the other dynamic length type, CStr also does this.
38    /// This function
39    /// - must NOT mutate the pointee
40    /// - must point to the entire datum's length (`size_of_val` must not lose bytes)
41    ///
42    /// Do not attempt to handle pass-by-value versus pass-by-ref in this fn's body!
43    /// A caller may be in a context where all types are handled by-reference, for instance.
44    unsafe fn point_from(ptr: ptr::NonNull<u8>) -> ptr::NonNull<Self>;
45
46    /// Cast a pointer to aligned varlena headers to this type
47    ///
48    /// This version allows you to assume the pointer is aligned to, and readable for, 4 bytes.
49    /// This optimization is not required. When in doubt, avoid implementing it, and rely on your
50    /// `point_from` implementation alone.
51    ///
52    /// # Safety
53    /// - This must be correctly invoked for the pointee type, as it may deref.
54    /// - This must be 4-byte aligned!
55    unsafe fn point_from_align4(ptr: ptr::NonNull<u32>) -> ptr::NonNull<Self> {
56        debug_assert!(ptr.is_aligned());
57        unsafe { BorrowDatum::point_from(ptr.cast()) }
58    }
59
60    /// Optimization for borrowing the referent
61    unsafe fn borrow_unchecked<'dat>(ptr: ptr::NonNull<u8>) -> &'dat Self {
62        unsafe { BorrowDatum::point_from(ptr).as_ref() }
63    }
64}
65
66/// From a pointer to a Datum, obtain a pointer to T's bytes
67///
68/// This may be None if T is PassBy::Ref
69///
70/// # Safety
71/// Assumes the Datum is init
72pub(crate) unsafe fn datum_ptr_to_bytes<T>(ptr: ptr::NonNull<Datum<'_>>) -> Option<ptr::NonNull<u8>>
73where
74    T: BorrowDatum,
75{
76    match T::PASS {
77        // Ptr<Datum> casts to Ptr<T>
78        PassBy::Value => Some(ptr.cast()),
79        // Ptr<Datum> derefs to Datum which to Ptr
80        PassBy::Ref => unsafe {
81            let datum = ptr.read();
82            ptr::NonNull::new(datum.sans_lifetime().cast_mut_ptr())
83        },
84    }
85}
86
87macro_rules! impl_borrow_fixed_len {
88    ($($value_ty:ty),*) => {
89        $(
90            unsafe impl BorrowDatum for $value_ty {
91                const PASS: PassBy = if mem::size_of::<Self>() <= mem::size_of::<Datum>() {
92                    PassBy::Value
93                } else {
94                    PassBy::Ref
95                };
96
97                unsafe fn point_from(ptr: ptr::NonNull<u8>) -> ptr::NonNull<Self> {
98                    ptr.cast()
99                }
100            }
101        )*
102    }
103}
104
105impl_borrow_fixed_len! {
106    i8, i16, i32, i64, bool, f32, f64,
107    pg_sys::Oid, pg_sys::Point,
108    Date, Time, Timestamp, TimestampWithTimeZone
109}
110
111/// It is rare to pass CStr via Datums, but not unheard of
112unsafe impl BorrowDatum for ffi::CStr {
113    const PASS: PassBy = PassBy::Ref;
114
115    unsafe fn point_from(ptr: ptr::NonNull<u8>) -> ptr::NonNull<Self> {
116        let char_ptr: *mut ffi::c_char = ptr.as_ptr().cast();
117        unsafe {
118            let len = ffi::CStr::from_ptr(char_ptr).to_bytes_with_nul().len();
119            ptr::NonNull::new_unchecked(ptr::slice_from_raw_parts_mut(char_ptr, len) as *mut Self)
120        }
121    }
122
123    unsafe fn borrow_unchecked<'dat>(ptr: ptr::NonNull<u8>) -> &'dat Self {
124        let char_ptr: *const ffi::c_char = ptr.as_ptr().cast();
125        unsafe { ffi::CStr::from_ptr(char_ptr) }
126    }
127}