pgrx/datum/
mod.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//! Handing for easily converting Postgres Datum types into their corresponding Rust types
11//! and converting Rust types into their corresponding Postgres types
12#![allow(clippy::borrow_interior_mutable_const, clippy::declare_interior_mutable_const)] // https://github.com/pgcentralfoundation/pgrx/issues/1526
13#![allow(clippy::needless_lifetimes)] // https://github.com/pgcentralfoundation/pgrx/issues?q=is%3Aissue+is%3Aopen+label%3Alifetime
14#![allow(clippy::unused_unit)]
15#![allow(clippy::unnecessary_fallible_conversions)] // https://github.com/pgcentralfoundation/pgrx/issues/1525
16mod anyarray;
17mod anyelement;
18mod array;
19mod borrow;
20mod date;
21pub mod datetime_support;
22mod from;
23mod geo;
24mod inet;
25mod internal;
26mod interval;
27mod into;
28mod json;
29pub mod numeric;
30pub mod numeric_support;
31#[deny(unsafe_op_in_unsafe_fn)]
32mod range;
33mod time;
34mod time_stamp;
35mod time_stamp_with_timezone;
36mod time_with_timezone;
37mod tuples;
38mod unbox;
39mod uuid;
40mod varlena;
41mod with_typeid;
42
43pub use self::time::*;
44pub use self::uuid::*;
45pub use anyarray::*;
46pub use anyelement::*;
47pub use array::*;
48pub use borrow::*;
49pub use date::*;
50pub use datetime_support::*;
51pub use from::*;
52pub use inet::*;
53pub use internal::*;
54pub use interval::*;
55pub use into::*;
56pub use json::*;
57pub use numeric::{AnyNumeric, Numeric};
58pub use range::*;
59pub use time_stamp::*;
60pub use time_stamp_with_timezone::*;
61pub use time_with_timezone::*;
62pub use unbox::*;
63pub use varlena::*;
64
65use crate::memcx::MemCx;
66use crate::pg_sys;
67use core::marker::PhantomData;
68use core::ptr;
69#[doc(hidden)]
70pub use with_typeid::nonstatic_typeid;
71pub use with_typeid::{WithArrayTypeIds, WithSizedTypeIds, WithTypeIds, WithVarlenaTypeIds};
72
73/// How Postgres represents datatypes
74///
75/// The "no-frills" version is [`pg_sys::Datum`], which is abstractly a union of "pointer to void"
76/// with other scalar types that can be packed within a pointer's bytes. In practical use, a "raw"
77/// Datum can prove to have the same risks as a pointer: code may try to use it without knowing
78/// whether its pointee has been deallocated. To lift a Datum into a Rust type requires making
79/// implicit lifetimes into explicit bounds.
80///
81/// Merely having a lifetime does not make `Datum<'src>` "safe" to use. To abstractly represent a
82/// full PostgreSQL value needs at least the tuple (Datum, bool, [`pg_sys::Oid`]): a tagged union.
83/// `Datum<'src>` itself is effectively a dynamically-typed union *without a type tag*. It exists
84/// not to make code manipulating it safe, but to make it possible to write unsafe code correctly,
85/// passing Datums to and from Postgres without having to wonder if the implied `&'src T` would
86/// actually refer to deallocated data.
87///
88/// # Designing safe abstractions
89/// A function must only be declared safe if *all* inputs **cannot** cause [undefined behavior].
90/// Transmuting a raw `pg_sys::Datum` into [`&'a T`] grants a potentially-unbounded lifetime,
91/// breaking the rule borrows must not outlive the borrowed. Avoiding such transmutations infects
92/// even simple generic functions with soundness obligations. Using only `&'a pg_sys::Datum` lasts
93/// only until one must pass by-value, which is the entire point of the original type as Postgres
94/// defined it, but can still be preferable.
95///
96/// `Datum<'src>` makes it theoretically possible to write functions with a signature like
97/// ```
98/// use pgrx::datum::Datum;
99/// # use core::marker::PhantomData;
100/// # use pgrx::memcx::MemCx;
101/// # struct InCx<'mcx, T>(T, PhantomData<&'mcx MemCx<'mcx>>);
102/// fn construct_type_from_datum<'src, T>(
103///     datum: Datum<'src>,
104///     func: impl FnOnce(Datum<'src>) -> InCx<'src, T>
105/// ) -> InCx<'src, T> {
106///    func(datum)
107/// }
108/// ```
109/// However, it is possible for `T<'src>` to be insufficient to represent the real lifetime of the
110/// abstract Postgres type's allocations. Often a Datum must be "detoasted", which may reallocate.
111/// This may demand two constraints on the return type to represent both possible lifetimes, like:
112/// ```
113/// use pgrx::datum::Datum;
114/// use pgrx::memcx::MemCx;
115/// # use core::marker::PhantomData;
116/// # struct Detoasted<'mcx, T>(T, PhantomData<&'mcx MemCx<'mcx>>);
117/// # struct InCx<'mcx, T>(T, PhantomData<&'mcx MemCx<'mcx>>);
118/// fn detoast_type_from_datum<'old, 'new, T>(
119///     datum: Datum<'old>,
120///     memcx: MemCx<'new>,
121/// ) -> Detoasted<'new, InCx<'old, T>> {
122///    todo!()
123/// }
124/// ```
125/// In actual practice, these can be unified into a single lifetime: the lower bound of both.
126/// This is both good and bad: types can use fewer lifetime annotations, even after detoasting.
127/// However, in general, because lifetime unification can be done implicitly by the compiler,
128/// it is often important to name each and every single lifetime involved in functions that
129/// perform these tasks.
130///
131/// [`&'a T`]: reference
132/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
133pub struct Datum<'src>(
134    pg_sys::Datum,
135    /// if a Datum borrows anything, it's "from" a [`pg_sys::MemoryContext`]
136    /// as a memory context, like an arena, is deallocated "together".
137    /// FIXME: a more-correct inner type later
138    PhantomData<&'src MemCx<'src>>,
139);
140
141impl<'src> Datum<'src> {
142    /// Strip a Datum of its lifetime for FFI purposes.
143    pub fn sans_lifetime(self) -> pg_sys::Datum {
144        self.0
145    }
146
147    /// Construct a Datum containing only a null pointer.
148    pub fn null() -> Datum<'src> {
149        Self(pg_sys::Datum::from(0), PhantomData)
150    }
151
152    /// Reborrow the Datum as `T`
153    ///
154    /// If the type is `PassBy::Ref`, this may be `None`.
155    pub unsafe fn borrow_as<T: BorrowDatum>(&self) -> Option<&T> {
156        let ptr = ptr::NonNull::new_unchecked(ptr::from_ref(self).cast_mut());
157        borrow::datum_ptr_to_bytes::<T>(ptr).map(|ptr| BorrowDatum::borrow_unchecked(ptr))
158    }
159}
160
161/// Represents a typed value as pair of [`Option<Datum>`] and [`Oid`].
162///
163/// [`Oid`]: pg_sys::Oid
164pub struct DatumWithOid<'src> {
165    datum: Option<Datum<'src>>,
166    oid: pg_sys::Oid,
167}
168
169impl<'src> DatumWithOid<'src> {
170    /// Construct a `DatumWithOid` containing the provided value and [`Oid`].
171    ///
172    /// [`Oid`]: pg_sys::Oid
173    pub unsafe fn new<T: IntoDatum>(value: T, oid: pg_sys::Oid) -> Self {
174        Self { datum: value.into_datum().map(|d| Datum(d, PhantomData::default())), oid }
175    }
176
177    /// Construct a `DatumWithOid` containing a null value for type `T`.
178    pub fn null<T: IntoDatum>() -> Self {
179        Self { datum: None, oid: T::type_oid() }
180    }
181
182    /// Returns an [`Option<Datum>`].
183    pub fn datum(&self) -> Option<Datum<'src>> {
184        self.datum.as_ref().map(|d| Datum(d.0, PhantomData::default()))
185    }
186
187    /// Returns an [`Oid`].
188    ///
189    /// [`Oid`]: pg_sys::Oid
190    pub fn oid(&self) -> pg_sys::Oid {
191        self.oid
192    }
193}
194
195impl<'src, T: IntoDatum> From<T> for DatumWithOid<'src> {
196    fn from(value: T) -> Self {
197        unsafe {
198            // SAFETY: The oid is provided by the type.
199            Self::new(value, T::type_oid())
200        }
201    }
202}
203
204/// A tagging trait to indicate a user type is also meant to be used by Postgres
205/// Implemented automatically by `#[derive(PostgresType)]`
206pub trait PostgresType {}
207
208/// Creates an array of [`pg_sys::Oid`] with the OID of each provided type
209///
210/// # Examples
211///
212/// ```
213/// use pgrx::{oids_of, datum::IntoDatum};
214///
215/// let oids = oids_of![i32, f64];
216/// assert_eq!(oids[0], i32::type_oid().into());
217/// assert_eq!(oids[1], f64::type_oid().into());
218///
219/// // the usual conversions or coercions are available
220/// let oid_vec = oids_of![i8, i16].to_vec();
221/// let no_oid = &oids_of![];
222/// assert_eq!(no_oid.len(), 0);
223/// ```
224#[macro_export]
225macro_rules! oids_of {
226    () =>(
227        // avoid coercions to an ambiguously-typed array or slice
228        [$crate::pg_sys::PgOid::Invalid; 0]
229    );
230    ($($t:path),+ $(,)?) => (
231        [$($crate::pg_sys::PgOid::from(<$t>::type_oid())),*]
232    );
233}