pgrx_pg_sys/
port.rs

1use crate as pg_sys;
2use core::mem::offset_of;
3use core::str::FromStr;
4
5/// this comes from `postgres_ext.h`
6pub const InvalidOid: crate::Oid = crate::Oid::INVALID;
7pub const InvalidOffsetNumber: super::OffsetNumber = 0;
8pub const FirstOffsetNumber: super::OffsetNumber = 1;
9pub const MaxOffsetNumber: super::OffsetNumber =
10    (super::BLCKSZ as usize / std::mem::size_of::<super::ItemIdData>()) as super::OffsetNumber;
11pub const InvalidBlockNumber: u32 = 0xFFFF_FFFF as crate::BlockNumber;
12pub const VARHDRSZ: usize = std::mem::size_of::<super::int32>();
13pub const InvalidTransactionId: super::TransactionId = 0 as super::TransactionId;
14pub const InvalidCommandId: super::CommandId = (!(0 as super::CommandId)) as super::CommandId;
15pub const FirstCommandId: super::CommandId = 0 as super::CommandId;
16pub const BootstrapTransactionId: super::TransactionId = 1 as super::TransactionId;
17pub const FrozenTransactionId: super::TransactionId = 2 as super::TransactionId;
18pub const FirstNormalTransactionId: super::TransactionId = 3 as super::TransactionId;
19pub const MaxTransactionId: super::TransactionId = 0xFFFF_FFFF as super::TransactionId;
20
21/// Given a valid HeapTuple pointer, return address of the user data
22///
23/// # Safety
24///
25/// This function cannot determine if the `tuple` argument is really a non-null pointer to a [`pg_sys::HeapTuple`].
26#[inline(always)]
27pub unsafe fn GETSTRUCT(tuple: crate::HeapTuple) -> *mut std::os::raw::c_char {
28    // #define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
29
30    // SAFETY:  The caller has asserted `tuple` is a valid HeapTuple and is properly aligned
31    // Additionally, t_data.t_hoff is an a u8, so it'll fit inside a usize
32    (*tuple).t_data.cast::<std::os::raw::c_char>().add((*(*tuple).t_data).t_hoff as _)
33}
34
35//
36// TODO: [`TYPEALIGN`] and [`MAXALIGN`] are also part of PR #948 and when that's all merged,
37//       their uses should be switched to these
38//
39
40#[allow(non_snake_case)]
41#[inline(always)]
42pub const unsafe fn TYPEALIGN(alignval: usize, len: usize) -> usize {
43    // #define TYPEALIGN(ALIGNVAL,LEN)  \
44    // (((uintptr_t) (LEN) + ((ALIGNVAL) - 1)) & ~((uintptr_t) ((ALIGNVAL) - 1)))
45    ((len) + ((alignval) - 1)) & !((alignval) - 1)
46}
47
48#[allow(non_snake_case)]
49#[inline(always)]
50pub const unsafe fn MAXALIGN(len: usize) -> usize {
51    // #define MAXALIGN(LEN) TYPEALIGN(MAXIMUM_ALIGNOF, (LEN))
52    TYPEALIGN(pg_sys::MAXIMUM_ALIGNOF as _, len)
53}
54
55///  Given a currently-allocated chunk of Postgres allocated memory, determine the context
56///  it belongs to.
57///
58/// All chunks allocated by any memory context manager are required to be
59/// preceded by the corresponding MemoryContext stored, without padding, in the
60/// preceding sizeof(void*) bytes.  A currently-allocated chunk must contain a
61/// backpointer to its owning context.  The backpointer is used by pfree() and
62/// repalloc() to find the context to call.
63///
64/// # Safety
65///
66/// The specified `pointer` **must** be one allocated by Postgres (via [`palloc`] and friends).
67///
68///
69/// # Panics
70///
71/// This function will panic if `pointer` is null, if it's not properly aligned, or if the memory
72/// it points to doesn't have a prefix that looks like a memory context pointer
73///
74/// [`palloc`]: crate::palloc
75#[allow(non_snake_case)]
76pub unsafe fn GetMemoryChunkContext(pointer: *mut std::os::raw::c_void) -> pg_sys::MemoryContext {
77    #[cfg(any(feature = "pg12", feature = "pg13", feature = "pg14", feature = "pg15"))]
78    {
79        // Postgres versions <16 don't export the "GetMemoryChunkContext" function.  It's a "static inline"
80        // function in `memutils.h`, so we port it to Rust right here
81        /*
82         * Try to detect bogus pointers handed to us, poorly though we can.
83         * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
84         * allocated chunk.
85         */
86        assert!(!pointer.is_null());
87        assert_eq!(pointer, MAXALIGN(pointer as usize) as *mut ::std::os::raw::c_void);
88
89        /*
90         * OK, it's probably safe to look at the context.
91         */
92        // 	context = *(MemoryContext *) (((char *) pointer) - sizeof(void *));
93        let context = unsafe {
94            // SAFETY: the caller has assured us that `pointer` points to palloc'd memory, which
95            // means it'll have this header before it
96            *(pointer
97                .cast::<::std::os::raw::c_char>()
98                .sub(std::mem::size_of::<*mut ::std::os::raw::c_void>())
99                .cast())
100        };
101
102        assert!(MemoryContextIsValid(context));
103
104        context
105    }
106    #[cfg(any(feature = "pg16", feature = "pg17"))]
107    {
108        #[pgrx_macros::pg_guard]
109        extern "C" {
110            #[link_name = "GetMemoryChunkContext"]
111            pub fn extern_fn(pointer: *mut std::os::raw::c_void) -> pg_sys::MemoryContext;
112        }
113        extern_fn(pointer)
114    }
115}
116
117/// Returns true if memory context is tagged correctly according to Postgres.
118///
119/// # Safety
120///
121/// The caller must only attempt this on a pointer to a Node.
122/// This may clarify if the pointee is correctly-initialized [`pg_sys::MemoryContextData`].
123///
124/// # Implementation Note
125///
126/// If Postgres adds more memory context types in the future, we need to do that here too.
127#[allow(non_snake_case)]
128#[inline(always)]
129pub unsafe fn MemoryContextIsValid(context: crate::MemoryContext) -> bool {
130    // #define MemoryContextIsValid(context) \
131    // 	((context) != NULL && \
132    // 	 (IsA((context), AllocSetContext) || \
133    // 	  IsA((context), SlabContext) || \
134    // 	  IsA((context), GenerationContext)))
135
136    !context.is_null()
137        && unsafe {
138            // SAFETY:  we just determined the pointer isn't null, and
139            // the caller asserts that it is being used on a Node.
140            let tag = (*context.cast::<crate::Node>()).type_;
141            use crate::NodeTag::*;
142            matches!(tag, T_AllocSetContext | T_SlabContext | T_GenerationContext)
143        }
144}
145
146pub const VARHDRSZ_EXTERNAL: usize = offset_of!(super::varattrib_1b_e, va_data);
147pub const VARHDRSZ_SHORT: usize = offset_of!(super::varattrib_1b, va_data);
148
149#[inline]
150pub fn get_pg_major_version_string() -> &'static str {
151    super::PG_MAJORVERSION.to_str().unwrap()
152}
153
154#[inline]
155pub fn get_pg_major_version_num() -> u16 {
156    u16::from_str(super::get_pg_major_version_string()).unwrap()
157}
158
159#[inline]
160pub fn get_pg_version_string() -> &'static str {
161    super::PG_VERSION_STR.to_str().unwrap()
162}
163
164#[inline]
165pub fn get_pg_major_minor_version_string() -> &'static str {
166    super::PG_VERSION.to_str().unwrap()
167}
168
169#[inline]
170pub fn TransactionIdIsNormal(xid: super::TransactionId) -> bool {
171    xid >= FirstNormalTransactionId
172}
173
174/// ```c
175///     #define type_is_array(typid)  (get_element_type(typid) != InvalidOid)
176/// ```
177#[inline]
178pub unsafe fn type_is_array(typoid: super::Oid) -> bool {
179    super::get_element_type(typoid) != InvalidOid
180}
181
182/// #define BufferGetPage(buffer) ((Page)BufferGetBlock(buffer))
183#[inline]
184pub unsafe fn BufferGetPage(buffer: crate::Buffer) -> crate::Page {
185    BufferGetBlock(buffer) as crate::Page
186}
187
188/// #define BufferGetBlock(buffer) \
189/// ( \
190///      AssertMacro(BufferIsValid(buffer)), \
191///      BufferIsLocal(buffer) ? \
192///            LocalBufferBlockPointers[-(buffer) - 1] \
193///      : \
194///            (Block) (BufferBlocks + ((Size) ((buffer) - 1)) * BLCKSZ) \
195/// )
196#[inline]
197pub unsafe fn BufferGetBlock(buffer: crate::Buffer) -> crate::Block {
198    if BufferIsLocal(buffer) {
199        *crate::LocalBufferBlockPointers.offset(((-buffer) - 1) as isize)
200    } else {
201        crate::BufferBlocks.add(((buffer as crate::Size) - 1) * crate::BLCKSZ as usize)
202            as crate::Block
203    }
204}
205
206/// #define BufferIsLocal(buffer)      ((buffer) < 0)
207#[inline]
208pub unsafe fn BufferIsLocal(buffer: crate::Buffer) -> bool {
209    buffer < 0
210}
211
212/// Retrieve the "user data" of the specified [`pg_sys::HeapTuple`] as a specific type. Typically this
213/// will be a struct that represents a Postgres system catalog, such as [`FormData_pg_class`].
214///
215/// # Returns
216///
217/// A pointer to the [`pg_sys::HeapTuple`]'s "user data", cast as a mutable pointer to `T`.  If the
218/// specified `htup` pointer is null, the null pointer is returned.
219///
220/// # Safety
221///
222/// This function cannot verify that the specified `htup` points to a valid [`pg_sys::HeapTuple`] nor
223/// that if it does, that its bytes are bitwise compatible with `T`.
224///
225/// [`FormData_pg_class`]: crate::FormData_pg_class
226#[inline]
227pub unsafe fn heap_tuple_get_struct<T>(htup: super::HeapTuple) -> *mut T {
228    if htup.is_null() {
229        std::ptr::null_mut()
230    } else {
231        unsafe {
232            // SAFETY:  The caller has told us `htop` is a valid HeapTuple
233            GETSTRUCT(htup).cast()
234        }
235    }
236}
237
238// All of this weird code is in response to Postgres having a relatively cavalier attitude about types:
239// - https://github.com/postgres/postgres/commit/1c27d16e6e5c1f463bbe1e9ece88dda811235165
240//
241// As a result, we redeclare their functions with the arguments they should have on earlier Postgres
242// and we route people to the old symbols they were using before on later ones.
243#[cfg(any(feature = "pg12", feature = "pg13", feature = "pg14", feature = "pg15"))]
244#[::pgrx_macros::pg_guard]
245extern "C" {
246    pub fn planstate_tree_walker(
247        planstate: *mut super::PlanState,
248        walker: ::core::option::Option<
249            unsafe extern "C" fn(*mut super::PlanState, *mut ::core::ffi::c_void) -> bool,
250        >,
251        context: *mut ::core::ffi::c_void,
252    ) -> bool;
253
254    pub fn query_tree_walker(
255        query: *mut super::Query,
256        walker: ::core::option::Option<
257            unsafe extern "C" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
258        >,
259        context: *mut ::core::ffi::c_void,
260        flags: ::core::ffi::c_int,
261    ) -> bool;
262
263    pub fn query_or_expression_tree_walker(
264        node: *mut super::Node,
265        walker: ::core::option::Option<
266            unsafe extern "C" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
267        >,
268        context: *mut ::core::ffi::c_void,
269        flags: ::core::ffi::c_int,
270    ) -> bool;
271
272    pub fn range_table_entry_walker(
273        rte: *mut super::RangeTblEntry,
274        walker: ::core::option::Option<
275            unsafe extern "C" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
276        >,
277        context: *mut ::core::ffi::c_void,
278        flags: ::core::ffi::c_int,
279    ) -> bool;
280
281    pub fn range_table_walker(
282        rtable: *mut super::List,
283        walker: ::core::option::Option<
284            unsafe extern "C" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
285        >,
286        context: *mut ::core::ffi::c_void,
287        flags: ::core::ffi::c_int,
288    ) -> bool;
289
290    pub fn expression_tree_walker(
291        node: *mut super::Node,
292        walker: ::core::option::Option<
293            unsafe extern "C" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
294        >,
295        context: *mut ::core::ffi::c_void,
296    ) -> bool;
297
298    pub fn raw_expression_tree_walker(
299        node: *mut super::Node,
300        walker: ::core::option::Option<
301            unsafe extern "C" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
302        >,
303        context: *mut ::core::ffi::c_void,
304    ) -> bool;
305}
306
307#[cfg(any(feature = "pg16", feature = "pg17"))]
308pub unsafe fn planstate_tree_walker(
309    planstate: *mut super::PlanState,
310    walker: ::core::option::Option<
311        unsafe extern "C" fn(*mut super::PlanState, *mut ::core::ffi::c_void) -> bool,
312    >,
313    context: *mut ::core::ffi::c_void,
314) -> bool {
315    crate::planstate_tree_walker_impl(planstate, walker, context)
316}
317
318#[cfg(any(feature = "pg16", feature = "pg17"))]
319pub unsafe fn query_tree_walker(
320    query: *mut super::Query,
321    walker: ::core::option::Option<
322        unsafe extern "C" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
323    >,
324    context: *mut ::core::ffi::c_void,
325    flags: ::core::ffi::c_int,
326) -> bool {
327    crate::query_tree_walker_impl(query, walker, context, flags)
328}
329
330#[cfg(any(feature = "pg16", feature = "pg17"))]
331pub unsafe fn query_or_expression_tree_walker(
332    node: *mut super::Node,
333    walker: ::core::option::Option<
334        unsafe extern "C" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
335    >,
336    context: *mut ::core::ffi::c_void,
337    flags: ::core::ffi::c_int,
338) -> bool {
339    crate::query_or_expression_tree_walker_impl(node, walker, context, flags)
340}
341
342#[cfg(any(feature = "pg16", feature = "pg17"))]
343pub unsafe fn expression_tree_walker(
344    node: *mut crate::Node,
345    walker: Option<unsafe extern "C" fn(*mut crate::Node, *mut ::core::ffi::c_void) -> bool>,
346    context: *mut ::core::ffi::c_void,
347) -> bool {
348    crate::expression_tree_walker_impl(node, walker, context)
349}
350
351#[cfg(any(feature = "pg16", feature = "pg17"))]
352pub unsafe fn range_table_entry_walker(
353    rte: *mut super::RangeTblEntry,
354    walker: ::core::option::Option<
355        unsafe extern "C" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
356    >,
357    context: *mut ::core::ffi::c_void,
358    flags: ::core::ffi::c_int,
359) -> bool {
360    crate::range_table_entry_walker_impl(rte, walker, context, flags)
361}
362
363#[cfg(any(feature = "pg16", feature = "pg17"))]
364pub unsafe fn range_table_walker(
365    rtable: *mut super::List,
366    walker: ::core::option::Option<
367        unsafe extern "C" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
368    >,
369    context: *mut ::core::ffi::c_void,
370    flags: ::core::ffi::c_int,
371) -> bool {
372    crate::range_table_walker_impl(rtable, walker, context, flags)
373}
374
375#[cfg(any(feature = "pg16", feature = "pg17"))]
376pub unsafe fn raw_expression_tree_walker(
377    node: *mut crate::Node,
378    walker: Option<unsafe extern "C" fn(*mut crate::Node, *mut ::core::ffi::c_void) -> bool>,
379    context: *mut ::core::ffi::c_void,
380) -> bool {
381    crate::raw_expression_tree_walker_impl(node, walker, context)
382}
383
384#[inline(always)]
385pub unsafe fn MemoryContextSwitchTo(context: crate::MemoryContext) -> crate::MemoryContext {
386    let old = crate::CurrentMemoryContext;
387
388    crate::CurrentMemoryContext = context;
389    old
390}