pgrx_pg_sys/submodules/
datum.rs

1//LICENSE Portions Copyright 2019-2021 ZomboDB, LLC.
2//LICENSE
3//LICENSE Portions Copyright 2021-2023 Technology Concepts & Design, Inc.
4//LICENSE
5//LICENSE Portions Copyright 2023-2023 PgCentral Foundation, Inc. <contact@pgcentral.org>
6//LICENSE
7//LICENSE All rights reserved.
8//LICENSE
9//LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file.
10// Polyfill while #![feature(strict_provenance)] is unstable
11use crate::NullableDatum;
12use std::ptr::NonNull;
13
14/// Postgres defines the "Datum" type as uintptr_t, so bindgen decides it is usize.
15/// Normally, this would be fine, except Postgres uses it more like void*:
16/// A pointer to anything that could mean anything, check your assumptions before using.
17///
18///
19/// Accordingly, the "Datum" type from bindgen is not entirely correct, as
20/// Rust's `usize` may match the size of `uintptr_t` but it is not quite the same.
21/// The compiler would rather know which integers are integers and which are pointers.
22/// As a result, Datum is now a wrapper around `*mut DatumBlob`.
23/// This type need not be exported unless the details of the type idiom become important.
24// This struct uses a Rust idiom invented before `extern type` was designed,
25// but should probably be replaced when #![feature(extern_type)] stabilizes
26#[repr(C)]
27struct DatumBlob {
28    _data: [u8; 0],
29    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
30}
31
32/// Datum is an abstract value that is effectively a union of all scalar types
33/// and all possible pointers in a Postgres context. That is, it is either
34/// "pass-by-value" (if the value fits into the platform's `uintptr_t`) or
35/// "pass-by-reference" (if it does not).
36///
37/// In Rust, it is best to treat this largely as a pointer while passing it around
38/// for code that doesn't care about what the Datum "truly is".
39/// If for some reason it is important to manipulate the address/value
40/// without "knowing the type" of the Datum, cast to a pointer and use pointer methods.
41///
42/// Only create Datums from non-pointers when you know you want to pass a value, as
43/// it is erroneous for `unsafe` code to dereference the address of "only a value" as a pointer.
44/// It is still a "safe" operation to create such pointers: validity is asserted by dereferencing,
45/// **or by creating a safe reference such as &T or &mut T**. Also be aware that the validity
46/// of Datum's Copy is premised on the same implicit issues with pointers being Copy:
47/// while any `&T` is live, other `*mut T` must not be used to write to that `&T`,
48/// and `&mut T` implies no other `*mut T` even exists outside an `&mut T`'s borrowing ancestry.
49/// It is thus of dubious soundness for Rust code to receive `*mut T`, create another `*mut T`,
50/// cast the first to `&mut T`, and then later try to use the second `*mut T` to write.
51/// It _is_ sound for Postgres itself to pass a copied pointer as a Datum to Rust code, then later
52/// to mutate that data through its original pointer after Rust creates and releases a `&mut T`.
53///
54/// For all intents and purposes, Postgres counts as `unsafe` code that may be relying
55/// on you communicating pointers correctly to it. Do not play games with your database.
56#[repr(transparent)]
57#[derive(Debug, Copy, Clone, PartialEq)]
58pub struct Datum(*mut DatumBlob);
59
60impl Datum {
61    /// Assume the datum is a value and extract the bits from
62    /// the memory address, interpreting them as an integer.
63    #[inline]
64    pub fn value(self) -> usize {
65        sptr::Strict::addr(self.0)
66    }
67
68    #[inline]
69    pub const fn null() -> Datum {
70        Datum(core::ptr::null_mut())
71    }
72
73    /// True if the datum is equal to the null pointer.
74    #[inline]
75    pub fn is_null(self) -> bool {
76        self.0.is_null()
77    }
78
79    /// Assume the datum is a pointer and cast it to point to T.
80    /// It is recommended to explicitly use `datum.cast_mut_ptr::<T>()`.
81    #[inline]
82    pub fn cast_mut_ptr<T>(self) -> *mut T {
83        self.0.cast()
84    }
85}
86
87impl From<usize> for Datum {
88    #[inline]
89    fn from(val: usize) -> Datum {
90        Datum(sptr::Strict::with_addr(NonNull::<DatumBlob>::dangling().as_ptr(), val))
91    }
92}
93
94impl From<Datum> for usize {
95    #[inline]
96    fn from(val: Datum) -> usize {
97        sptr::Strict::addr(val.0)
98    }
99}
100
101impl From<isize> for Datum {
102    #[inline]
103    fn from(val: isize) -> Datum {
104        Datum::from(val as usize)
105    }
106}
107
108impl From<u8> for Datum {
109    #[inline]
110    fn from(val: u8) -> Datum {
111        Datum::from(usize::from(val))
112    }
113}
114
115impl From<u16> for Datum {
116    #[inline]
117    fn from(val: u16) -> Datum {
118        Datum::from(usize::from(val))
119    }
120}
121
122impl From<u32> for Datum {
123    #[inline]
124    fn from(val: u32) -> Datum {
125        Datum::from(val as usize)
126    }
127}
128
129impl From<u64> for Datum {
130    #[inline]
131    fn from(val: u64) -> Datum {
132        if cfg!(target_pointer_width = "64") {
133            Datum::from(val as usize)
134        } else {
135            unsafe {
136                let ptr = crate::palloc(size_of::<u64>()) as *mut u64;
137                *ptr = val;
138                Datum::from(ptr)
139            }
140        }
141    }
142}
143
144impl From<i8> for Datum {
145    #[inline]
146    fn from(val: i8) -> Datum {
147        Datum::from(isize::from(val))
148    }
149}
150
151impl From<i16> for Datum {
152    #[inline]
153    fn from(val: i16) -> Datum {
154        Datum::from(isize::from(val))
155    }
156}
157
158impl From<i32> for Datum {
159    #[inline]
160    fn from(val: i32) -> Datum {
161        Datum::from(val as usize)
162    }
163}
164
165impl From<i64> for Datum {
166    #[inline]
167    fn from(val: i64) -> Datum {
168        if cfg!(target_pointer_width = "64") {
169            Datum::from(val as usize)
170        } else {
171            unsafe {
172                let ptr = crate::palloc(size_of::<i64>()) as *mut i64;
173                *ptr = val;
174                Datum::from(ptr)
175            }
176        }
177    }
178}
179
180impl From<bool> for Datum {
181    #[inline]
182    fn from(val: bool) -> Datum {
183        Datum::from(val as usize)
184    }
185}
186
187impl<T> From<*mut T> for Datum {
188    #[inline]
189    fn from(val: *mut T) -> Datum {
190        Datum(val.cast())
191    }
192}
193
194impl<T> From<*const T> for Datum {
195    #[inline]
196    fn from(val: *const T) -> Datum {
197        Datum(val as *mut _)
198    }
199}
200
201impl<T> PartialEq<*mut T> for Datum {
202    #[inline]
203    fn eq(&self, other: &*mut T) -> bool {
204        &self.0.cast() == other
205    }
206}
207
208impl<T> PartialEq<Datum> for *mut T {
209    #[inline]
210    fn eq(&self, other: &Datum) -> bool {
211        self == &other.0.cast()
212    }
213}
214
215impl TryFrom<NullableDatum> for Datum {
216    type Error = ();
217
218    #[inline]
219    fn try_from(nd: NullableDatum) -> Result<Datum, ()> {
220        let NullableDatum { value, isnull } = nd;
221        if isnull {
222            Err(())
223        } else {
224            Ok(value)
225        }
226    }
227}
228
229impl From<NullableDatum> for Option<Datum> {
230    #[inline]
231    fn from(nd: NullableDatum) -> Option<Datum> {
232        Datum::try_from(nd).ok()
233    }
234}
235
236#[cfg(test)]
237mod test {
238    use super::*;
239
240    #[test]
241    fn roundtrip_integers() {
242        let val = i64::MAX;
243        let datum = Datum::from(val);
244        assert_eq!(datum.value() as i64, val);
245
246        let val = isize::MAX;
247        let datum = Datum::from(val);
248        assert_eq!(datum.value() as isize, val);
249
250        let val = i64::MIN;
251        let datum = Datum::from(val);
252        assert_eq!(datum.value() as i64, val);
253
254        let val = isize::MIN;
255        let datum = Datum::from(val);
256        assert_eq!(datum.value() as isize, val);
257
258        let val = u64::MAX;
259        let datum = Datum::from(val);
260        assert_eq!(datum.value() as u64, val);
261
262        let val = usize::MAX;
263        let datum = Datum::from(val);
264        assert_eq!(datum.value(), val);
265    }
266}