pgrx/datum/
unbox.rs

1use super::uuid::Uuid;
2use super::Datum;
3use crate::prelude::*;
4use crate::varlena::{text_to_rust_str_unchecked, varlena_to_byte_slice};
5use crate::{Json, JsonB};
6use alloc::ffi::CString;
7use core::ffi::CStr;
8
9/// Directly convert a Datum into this type
10///
11/// Previously, pgrx used FromDatum exclusively, which captures conversion into T, but
12/// leaves ambiguous a number of possibilities and allows large swathes of behavior to
13/// be folded under a single trait. This provided certain beneficial ergonomics at first,
14/// but eventually behavior was incorrectly folded under FromDatum, which provided the
15/// illusion of soundness.
16///
17/// UnboxDatum should only be implemented for a type that CAN be directly converted from a Datum,
18/// and it doesn't say whether it can be used directly or if it should be detoasted via MemCx.
19/// It's currently just a possibly-temporary shim to make pgrx work.
20///
21/// # Safety
22/// This trait is used to bound the lifetime of certain types: thus the associated type must be
23/// this type but "infected" by the Datum's lifetime. By implementing this, you verify that you
24/// are implementing this in the way that satisfies that lifetime constraint. There isn't really
25/// a good way to constrain lifetimes correctly without forcing from-Datum types to go through a
26/// wrapper type bound by the lifetime of the Datum. And what would you use as the bound, hmm?
27pub unsafe trait UnboxDatum {
28    // TODO: Currently, this doesn't actually get used to identify all the cases where the Datum
29    // is actually a pointer type. However, it has been noted that Postgres does yield nullptr
30    // on occasion, even when they say something is not supposed to be nullptr. As it is common
31    // for Postgres to init [Datum<'_>] with palloc0, it is reasonable to assume nullptr is a risk,
32    // even if `is_null == false`.
33    //
34    // Wait, what are you about, Jubilee? In some cases, the chance of nullness doesn't exist!
35    // This is because we are materializing the datums from e.g. pointers to an Array, which
36    // requires you to have had a valid base pointer into an ArrayType to start!
37    // That's why you started using this goofy GAT scheme in the first place!
38    /// Self with the lifetime `'src`
39    ///
40    /// The lifetime `'src` represents the lifetime of the "root" source of this Datum, which
41    /// may be a memory context or a shorter-lived type inside that context.
42    ///
43    type As<'src>
44    where
45        Self: 'src;
46    /// Convert from `Datum<'src>` to `T::As<'src>`
47    ///
48    /// This should be the direct conversion in each case. It is very unsafe to use directly.
49    ///
50    /// # Safety
51    /// Due to the absence of an `is_null` parameter, this does not validate "SQL nullness" of
52    /// the Datum in question. The intention is that this core fn eschews an additional branch.
53    /// Just... don't use it if it might be null?
54    ///
55    /// This also should not be used as the primary conversion mechanism if it requires a MemCx,
56    /// as this is intended primarily to be used in cases where the datum is guaranteed to be
57    /// detoasted already.
58    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
59    where
60        Self: 'src;
61}
62
63macro_rules! unbox_int {
64    ($($int_ty:ty),*) => {
65        $(
66            unsafe impl UnboxDatum for $int_ty {
67                type As<'src> = $int_ty;
68                unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src> where Self: 'src {
69                    datum.0.value() as $int_ty
70                }
71            }
72        )*
73    }
74}
75
76unbox_int! {
77    i8, i16, i32, i64
78}
79
80unsafe impl UnboxDatum for bool {
81    type As<'src> = bool;
82    #[inline]
83    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
84    where
85        Self: 'src,
86    {
87        datum.0.value() != 0
88    }
89}
90
91unsafe impl UnboxDatum for f32 {
92    type As<'src> = f32;
93    #[inline]
94    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
95    where
96        Self: 'src,
97    {
98        f32::from_bits(datum.0.value() as u32)
99    }
100}
101
102unsafe impl UnboxDatum for f64 {
103    type As<'src> = f64;
104    #[inline]
105    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
106    where
107        Self: 'src,
108    {
109        f64::from_bits(datum.0.value() as u64)
110    }
111}
112
113unsafe impl UnboxDatum for str {
114    type As<'src> = &'src str;
115    #[inline]
116    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
117    where
118        Self: 'src,
119    {
120        unsafe { text_to_rust_str_unchecked(datum.0.cast_mut_ptr()) }
121    }
122}
123
124unsafe impl UnboxDatum for &str {
125    #[rustfmt::skip]
126    type As<'src> = &'src str where Self: 'src;
127    #[inline]
128    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
129    where
130        Self: 'src,
131    {
132        unsafe { text_to_rust_str_unchecked(datum.0.cast_mut_ptr()) }
133    }
134}
135
136unsafe impl UnboxDatum for CStr {
137    type As<'src> = &'src CStr;
138    #[inline]
139    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
140    where
141        Self: 'src,
142    {
143        unsafe { CStr::from_ptr(datum.0.cast_mut_ptr()) }
144    }
145}
146
147unsafe impl UnboxDatum for &CStr {
148    #[rustfmt::skip]
149    type As<'src> = &'src CStr where Self: 'src;
150    #[inline]
151    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
152    where
153        Self: 'src,
154    {
155        unsafe { CStr::from_ptr(datum.0.cast_mut_ptr()) }
156    }
157}
158
159unsafe impl UnboxDatum for [u8] {
160    type As<'src> = &'src [u8];
161    #[inline]
162    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
163    where
164        Self: 'src,
165    {
166        unsafe { varlena_to_byte_slice(datum.0.cast_mut_ptr()) }
167    }
168}
169
170unsafe impl UnboxDatum for &[u8] {
171    #[rustfmt::skip]
172    type As<'src> = &'src [u8] where Self: 'src;
173    #[inline]
174    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
175    where
176        Self: 'src,
177    {
178        unsafe { varlena_to_byte_slice(datum.0.cast_mut_ptr()) }
179    }
180}
181
182unsafe impl UnboxDatum for pg_sys::Oid {
183    type As<'src> = pg_sys::Oid;
184    #[inline]
185    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
186    where
187        Self: 'src,
188    {
189        pg_sys::Oid::from(datum.0.value() as u32)
190    }
191}
192
193unsafe impl UnboxDatum for String {
194    type As<'src> = String;
195    #[inline]
196    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
197    where
198        Self: 'src,
199    {
200        unsafe { str::unbox(datum) }.to_owned()
201    }
202}
203
204unsafe impl UnboxDatum for CString {
205    type As<'src> = CString;
206    #[inline]
207    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
208    where
209        Self: 'src,
210    {
211        unsafe { CStr::unbox(datum) }.to_owned()
212    }
213}
214
215unsafe impl UnboxDatum for pg_sys::Datum {
216    type As<'src> = pg_sys::Datum;
217    #[inline]
218    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
219    where
220        Self: 'src,
221    {
222        datum.0
223    }
224}
225
226unsafe impl UnboxDatum for Uuid {
227    type As<'src> = Uuid;
228    #[inline]
229    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
230    where
231        Self: 'src,
232    {
233        Uuid::from_bytes(datum.0.cast_mut_ptr::<[u8; 16]>().read())
234    }
235}
236
237unsafe impl UnboxDatum for Date {
238    type As<'src> = Date;
239    #[inline]
240    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
241    where
242        Self: 'src,
243    {
244        Date::try_from(i32::unbox(datum)).unwrap_unchecked()
245    }
246}
247
248unsafe impl UnboxDatum for Time {
249    type As<'src> = Time;
250    #[inline]
251    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
252    where
253        Self: 'src,
254    {
255        Time::try_from(i64::unbox(datum)).unwrap_unchecked()
256    }
257}
258
259unsafe impl UnboxDatum for Timestamp {
260    type As<'src> = Timestamp;
261    #[inline]
262    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
263    where
264        Self: 'src,
265    {
266        Timestamp::try_from(i64::unbox(datum)).unwrap_unchecked()
267    }
268}
269
270unsafe impl UnboxDatum for TimestampWithTimeZone {
271    type As<'src> = TimestampWithTimeZone;
272    #[inline]
273    unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src>
274    where
275        Self: 'src,
276    {
277        TimestampWithTimeZone::try_from(i64::unbox(datum)).unwrap_unchecked()
278    }
279}
280
281macro_rules! unbox_with_fromdatum {
282    ($($from_ty:ty,)*) => {
283        $(
284            unsafe impl UnboxDatum for $from_ty {
285                type As<'src> = $from_ty;
286                unsafe fn unbox<'src>(datum: Datum<'src>) -> Self::As<'src> where Self: 'src {
287                    Self::from_datum(datum.0, false).unwrap()
288                }
289            }
290        )*
291    }
292}
293
294unbox_with_fromdatum! {
295    TimeWithTimeZone, AnyNumeric, char, pg_sys::Point, Interval, pg_sys::BOX, pg_sys::ItemPointerData,
296}
297
298unsafe impl UnboxDatum for PgHeapTuple<'_, crate::AllocatedByRust> {
299    #[rustfmt::skip]
300    type As<'src> = PgHeapTuple<'src, AllocatedByRust> where Self: 'src;
301    #[inline]
302    unsafe fn unbox<'src>(d: Datum<'src>) -> Self::As<'src>
303    where
304        Self: 'src,
305    {
306        PgHeapTuple::from_datum(d.0, false).unwrap()
307    }
308}
309
310unsafe impl<T: FromDatum + UnboxDatum> UnboxDatum for Array<'_, T> {
311    #[rustfmt::skip]
312    type As<'src> = Array<'src, T> where Self: 'src;
313    unsafe fn unbox<'src>(d: Datum<'src>) -> Array<'src, T>
314    where
315        Self: 'src,
316    {
317        Array::from_datum(d.0, false).unwrap()
318    }
319}
320
321unsafe impl<T: FromDatum + UnboxDatum> UnboxDatum for VariadicArray<'_, T> {
322    #[rustfmt::skip]
323    type As<'src> = VariadicArray<'src, T> where Self: 'src;
324    unsafe fn unbox<'src>(d: Datum<'src>) -> VariadicArray<'src, T>
325    where
326        Self: 'src,
327    {
328        VariadicArray::from_datum(d.0, false).unwrap()
329    }
330}
331
332unsafe impl<T: FromDatum + UnboxDatum + RangeSubType> UnboxDatum for Range<T> {
333    #[rustfmt::skip]
334    type As<'src> = Range<T> where Self: 'src;
335    unsafe fn unbox<'src>(d: Datum<'src>) -> Self::As<'src>
336    where
337        Self: 'src,
338    {
339        Range::<T>::from_datum(d.0, false).unwrap()
340    }
341}
342
343unsafe impl<const P: u32, const S: u32> UnboxDatum for Numeric<P, S> {
344    type As<'src> = Numeric<P, S>;
345    #[inline]
346    unsafe fn unbox<'src>(d: Datum<'src>) -> Self::As<'src>
347    where
348        Self: 'src,
349    {
350        Numeric::from_datum(d.0, false).unwrap()
351    }
352}
353
354unsafe impl<T> UnboxDatum for PgBox<T, AllocatedByPostgres> {
355    #[rustfmt::skip]
356    type As<'src> = PgBox<T> where Self: 'src;
357    #[inline]
358    unsafe fn unbox<'src>(d: Datum<'src>) -> Self::As<'src>
359    where
360        Self: 'src,
361    {
362        PgBox::from_datum(d.0, false).unwrap()
363    }
364}
365
366unsafe impl UnboxDatum for Json {
367    #[rustfmt::skip]
368    type As<'src> = Json where Self: 'src;
369    #[inline]
370    unsafe fn unbox<'src>(d: Datum<'src>) -> Self::As<'src>
371    where
372        Self: 'src,
373    {
374        Json::from_datum(d.0, false).unwrap()
375    }
376}
377
378unsafe impl UnboxDatum for JsonB {
379    #[rustfmt::skip]
380    type As<'src> = JsonB where Self: 'src;
381    #[inline]
382    unsafe fn unbox<'src>(d: Datum<'src>) -> Self::As<'src>
383    where
384        Self: 'src,
385    {
386        JsonB::from_datum(d.0, false).unwrap()
387    }
388}