pgx_pg_sys/submodules/
datum.rs

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