Skip to main content

pgrx_pg_sys/
port.rs

1use crate as pg_sys;
2use crate::BLCKSZ;
3use core::mem::offset_of;
4use core::str::FromStr;
5
6/// this comes from `postgres_ext.h`
7pub const InvalidOid: crate::Oid = crate::Oid::INVALID;
8pub const InvalidOffsetNumber: super::OffsetNumber = 0;
9pub const FirstOffsetNumber: super::OffsetNumber = 1;
10pub const MaxOffsetNumber: super::OffsetNumber =
11    (super::BLCKSZ as usize / std::mem::size_of::<super::ItemIdData>()) as super::OffsetNumber;
12pub const InvalidBlockNumber: u32 = 0xFFFF_FFFF as crate::BlockNumber;
13pub const VARHDRSZ: usize = std::mem::size_of::<super::int32>();
14pub const InvalidCommandId: super::CommandId = (!(0 as super::CommandId)) as super::CommandId;
15pub const FirstCommandId: super::CommandId = 0 as super::CommandId;
16pub const InvalidTransactionId: crate::TransactionId = crate::TransactionId::INVALID;
17pub const BootstrapTransactionId: crate::TransactionId = crate::TransactionId::BOOTSTRAP;
18pub const FrozenTransactionId: crate::TransactionId = crate::TransactionId::FROZEN;
19pub const FirstNormalTransactionId: crate::TransactionId = crate::TransactionId::FIRST_NORMAL;
20pub const MaxTransactionId: crate::TransactionId = crate::TransactionId::MAX;
21
22/// Given a valid HeapTuple pointer, return address of the user data
23///
24/// # Safety
25///
26/// This function cannot determine if the `tuple` argument is really a non-null pointer to a [`pg_sys::HeapTuple`].
27#[inline(always)]
28pub unsafe fn GETSTRUCT(tuple: crate::HeapTuple) -> *mut std::os::raw::c_char {
29    // #define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
30
31    // SAFETY:  The caller has asserted `tuple` is a valid HeapTuple and is properly aligned
32    // Additionally, t_data.t_hoff is an a u8, so it'll fit inside a usize
33    (*tuple).t_data.cast::<std::os::raw::c_char>().add((*(*tuple).t_data).t_hoff as _)
34}
35
36//
37// TODO: [`TYPEALIGN`] and [`MAXALIGN`] are also part of PR #948 and when that's all merged,
38//       their uses should be switched to these
39//
40
41#[allow(non_snake_case)]
42#[inline(always)]
43pub const unsafe fn TYPEALIGN(alignval: usize, len: usize) -> usize {
44    // #define TYPEALIGN(ALIGNVAL,LEN)  \
45    // (((uintptr_t) (LEN) + ((ALIGNVAL) - 1)) & ~((uintptr_t) ((ALIGNVAL) - 1)))
46    ((len) + ((alignval) - 1)) & !((alignval) - 1)
47}
48
49#[allow(non_snake_case)]
50#[inline(always)]
51pub const unsafe fn MAXALIGN(len: usize) -> usize {
52    // #define MAXALIGN(LEN) TYPEALIGN(MAXIMUM_ALIGNOF, (LEN))
53    TYPEALIGN(pg_sys::MAXIMUM_ALIGNOF as _, len)
54}
55
56///  Given a currently-allocated chunk of Postgres allocated memory, determine the context
57///  it belongs to.
58///
59/// All chunks allocated by any memory context manager are required to be
60/// preceded by the corresponding MemoryContext stored, without padding, in the
61/// preceding sizeof(void*) bytes.  A currently-allocated chunk must contain a
62/// backpointer to its owning context.  The backpointer is used by pfree() and
63/// repalloc() to find the context to call.
64///
65/// # Safety
66///
67/// The specified `pointer` **must** be one allocated by Postgres (via [`palloc`] and friends).
68///
69///
70/// # Panics
71///
72/// This function will panic if `pointer` is null, if it's not properly aligned, or if the memory
73/// it points to doesn't have a prefix that looks like a memory context pointer
74///
75/// [`palloc`]: crate::palloc
76#[allow(non_snake_case)]
77pub unsafe fn GetMemoryChunkContext(pointer: *mut std::os::raw::c_void) -> pg_sys::MemoryContext {
78    #[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
79    {
80        // Postgres versions <16 don't export the "GetMemoryChunkContext" function.  It's a "static inline"
81        // function in `memutils.h`, so we port it to Rust right here
82        /*
83         * Try to detect bogus pointers handed to us, poorly though we can.
84         * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
85         * allocated chunk.
86         */
87        assert!(!pointer.is_null());
88        assert_eq!(pointer, MAXALIGN(pointer as usize) as *mut ::std::os::raw::c_void);
89
90        /*
91         * OK, it's probably safe to look at the context.
92         */
93        // 	context = *(MemoryContext *) (((char *) pointer) - sizeof(void *));
94        let context = unsafe {
95            // SAFETY: the caller has assured us that `pointer` points to palloc'd memory, which
96            // means it'll have this header before it
97            *(pointer
98                .cast::<::std::os::raw::c_char>()
99                .sub(std::mem::size_of::<*mut ::std::os::raw::c_void>())
100                .cast())
101        };
102
103        assert!(MemoryContextIsValid(context));
104
105        context
106    }
107    #[cfg(any(feature = "pg16", feature = "pg17", feature = "pg18", feature = "pg19"))]
108    {
109        #[pgrx_macros::pg_guard]
110        unsafe extern "C-unwind" {
111            #[link_name = "GetMemoryChunkContext"]
112            pub fn extern_fn(pointer: *mut std::os::raw::c_void) -> pg_sys::MemoryContext;
113        }
114        extern_fn(pointer)
115    }
116}
117
118/// Returns true if memory context is tagged correctly according to Postgres.
119///
120/// # Safety
121///
122/// The caller must only attempt this on a pointer to a Node.
123/// This may clarify if the pointee is correctly-initialized [`pg_sys::MemoryContextData`].
124///
125/// # Implementation Note
126///
127/// If Postgres adds more memory context types in the future, we need to do that here too.
128#[allow(non_snake_case)]
129#[inline(always)]
130pub unsafe fn MemoryContextIsValid(context: crate::MemoryContext) -> bool {
131    // #define MemoryContextIsValid(context) \
132    // 	((context) != NULL && \
133    // 	 (IsA((context), AllocSetContext) || \
134    // 	  IsA((context), SlabContext) || \
135    // 	  IsA((context), GenerationContext)))
136
137    !context.is_null()
138        && unsafe {
139            // SAFETY:  we just determined the pointer isn't null, and
140            // the caller asserts that it is being used on a Node.
141            let tag = (*context.cast::<crate::Node>()).type_;
142            use crate::NodeTag::*;
143            matches!(tag, T_AllocSetContext | T_SlabContext | T_GenerationContext)
144        }
145}
146
147pub const VARHDRSZ_EXTERNAL: usize = offset_of!(super::varattrib_1b_e, va_data);
148pub const VARHDRSZ_SHORT: usize = offset_of!(super::varattrib_1b, va_data);
149
150#[inline]
151pub fn get_pg_major_version_string() -> &'static str {
152    super::PG_MAJORVERSION.to_str().unwrap()
153}
154
155#[inline]
156pub fn get_pg_major_version_num() -> u16 {
157    u16::from_str(super::get_pg_major_version_string()).unwrap()
158}
159
160#[cfg(any(not(target_env = "msvc"), feature = "pg17", feature = "pg18", feature = "pg19"))]
161#[inline]
162pub fn get_pg_version_string() -> &'static str {
163    super::PG_VERSION_STR.to_str().unwrap()
164}
165
166#[cfg(all(
167    target_env = "msvc",
168    any(feature = "pg13", feature = "pg14", feature = "pg15", feature = "pg16")
169))]
170#[inline]
171pub fn get_pg_version_string() -> &'static str {
172    // bindgen cannot get value of PG_VERSION_STR
173    // PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit
174    static PG_VERSION_STR: [u8; 256] = const {
175        let major = super::PG_MAJORVERSION_NUM;
176        let minor = super::PG_MINORVERSION_NUM;
177        #[cfg(target_pointer_width = "32")]
178        let pointer_width = 32_u32;
179        #[cfg(target_pointer_width = "64")]
180        let pointer_width = 64_u32;
181        // a fake value
182        let msc_ver = b"1700";
183        let mut buffer = [0u8; 256];
184        let mut pointer = 0;
185        {
186            let s = b"PostgreSQL ";
187            let mut i = 0;
188            while i < s.len() {
189                buffer[pointer + i] = s[i];
190                i += 1;
191            }
192            pointer += s.len();
193        }
194        {
195            buffer[pointer + 0] = b'0' + (major / 10) as u8;
196            buffer[pointer + 1] = b'0' + (major % 10) as u8;
197            pointer += 2;
198        }
199        {
200            let s = b".";
201            let mut i = 0;
202            while i < s.len() {
203                buffer[pointer + i] = s[i];
204                i += 1;
205            }
206            pointer += s.len();
207        }
208        if minor < 10 {
209            buffer[pointer + 0] = b'0' + (minor % 10) as u8;
210            pointer += 1;
211        } else {
212            buffer[pointer + 0] = b'0' + (minor / 10) as u8;
213            buffer[pointer + 1] = b'0' + (minor % 10) as u8;
214            pointer += 2;
215        }
216        {
217            let s = b", compiled by Visual C++ build ";
218            let mut i = 0;
219            while i < s.len() {
220                buffer[pointer + i] = s[i];
221                i += 1;
222            }
223            pointer += s.len();
224        }
225        {
226            let s = msc_ver;
227            let mut i = 0;
228            while i < s.len() {
229                buffer[pointer + i] = s[i];
230                i += 1;
231            }
232            pointer += s.len();
233        }
234        {
235            let s = b", ";
236            let mut i = 0;
237            while i < s.len() {
238                buffer[pointer + i] = s[i];
239                i += 1;
240            }
241            pointer += s.len();
242        }
243        {
244            buffer[pointer + 0] = b'0' + (pointer_width / 10) as u8;
245            buffer[pointer + 1] = b'0' + (pointer_width % 10) as u8;
246            pointer += 2;
247        }
248        {
249            let s = b"-bit";
250            let mut i = 0;
251            while i < s.len() {
252                buffer[pointer + i] = s[i];
253                i += 1;
254            }
255            pointer += s.len();
256        }
257        buffer[pointer] = 0;
258        buffer
259    };
260    unsafe { std::ffi::CStr::from_ptr(PG_VERSION_STR.as_ptr().cast()).to_str().unwrap() }
261}
262
263#[inline]
264pub fn get_pg_major_minor_version_string() -> &'static str {
265    super::PG_VERSION.to_str().unwrap()
266}
267
268#[inline]
269pub fn TransactionIdIsNormal(xid: super::TransactionId) -> bool {
270    xid >= FirstNormalTransactionId
271}
272
273/// `TransactionIdPrecedes` --- is id1 logically < id2?
274///
275/// Postgres 19 turned this into a `static inline`, so we implement it ourselves
276#[cfg(feature = "pg19")]
277#[inline]
278pub unsafe fn TransactionIdPrecedes(id1: super::TransactionId, id2: super::TransactionId) -> bool {
279    // If either ID is a permanent XID then we can just do unsigned comparison.
280    // If both are normal, do a modulo-2^32 comparison.
281    if !TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2) {
282        return id1 < id2;
283    }
284
285    (id1.into_inner().wrapping_sub(id2.into_inner()) as i32) < 0
286}
287
288/// `TransactionIdPrecedesOrEquals` --- is id1 logically <= id2?
289///
290/// Postgres 19 turned this into a `static inline`, so we implement it ourselves
291#[cfg(feature = "pg19")]
292#[inline]
293pub unsafe fn TransactionIdPrecedesOrEquals(
294    id1: super::TransactionId,
295    id2: super::TransactionId,
296) -> bool {
297    if !TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2) {
298        return id1 <= id2;
299    }
300
301    (id1.into_inner().wrapping_sub(id2.into_inner()) as i32) <= 0
302}
303
304/// `TransactionIdFollows` --- is id1 logically > id2?
305///
306/// Postgres 19 turned this into a `static inline`, so we implement it ourselves
307#[cfg(feature = "pg19")]
308#[inline]
309pub unsafe fn TransactionIdFollows(id1: super::TransactionId, id2: super::TransactionId) -> bool {
310    if !TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2) {
311        return id1 > id2;
312    }
313
314    (id1.into_inner().wrapping_sub(id2.into_inner()) as i32) > 0
315}
316
317/// `TransactionIdFollowsOrEquals` --- is id1 logically >= id2?
318///
319/// Postgres 19 turned this into a `static inline`, so we implement it ourselves
320#[cfg(feature = "pg19")]
321#[inline]
322pub unsafe fn TransactionIdFollowsOrEquals(
323    id1: super::TransactionId,
324    id2: super::TransactionId,
325) -> bool {
326    if !TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2) {
327        return id1 >= id2;
328    }
329
330    (id1.into_inner().wrapping_sub(id2.into_inner()) as i32) >= 0
331}
332
333/// ```c
334///     #define type_is_array(typid)  (get_element_type(typid) != InvalidOid)
335/// ```
336#[inline]
337pub unsafe fn type_is_array(typoid: super::Oid) -> bool {
338    super::get_element_type(typoid) != InvalidOid
339}
340
341/// #define BufferGetPage(buffer) ((Page)BufferGetBlock(buffer))
342#[inline]
343#[cfg(any(
344    feature = "pg13",
345    feature = "pg14",
346    feature = "pg15",
347    all(
348        not(feature = "cshim"),
349        any(feature = "pg16", feature = "pg17", feature = "pg18", feature = "pg19")
350    )
351))]
352pub unsafe fn BufferGetPage(buffer: crate::Buffer) -> crate::Page {
353    BufferGetBlock(buffer) as crate::Page
354}
355
356/// #define BufferGetBlock(buffer) \
357/// ( \
358///      AssertMacro(BufferIsValid(buffer)), \
359///      BufferIsLocal(buffer) ? \
360///            LocalBufferBlockPointers[-(buffer) - 1] \
361///      : \
362///            (Block) (BufferBlocks + ((Size) ((buffer) - 1)) * BLCKSZ) \
363/// )
364#[inline]
365#[cfg(any(
366    feature = "pg13",
367    feature = "pg14",
368    feature = "pg15",
369    all(
370        not(feature = "cshim"),
371        any(feature = "pg16", feature = "pg17", feature = "pg18", feature = "pg19")
372    )
373))]
374pub unsafe fn BufferGetBlock(buffer: crate::Buffer) -> crate::Block {
375    if BufferIsLocal(buffer) {
376        *crate::LocalBufferBlockPointers.offset(((-buffer) - 1) as isize)
377    } else {
378        crate::BufferBlocks.add(((buffer as crate::Size) - 1) * crate::BLCKSZ as usize)
379            as crate::Block
380    }
381}
382
383/// #define BufferIsLocal(buffer)      ((buffer) < 0)
384#[inline]
385pub unsafe fn BufferIsLocal(buffer: crate::Buffer) -> bool {
386    buffer < 0
387}
388
389/// Retrieve the "user data" of the specified [`pg_sys::HeapTuple`] as a specific type. Typically this
390/// will be a struct that represents a Postgres system catalog, such as [`FormData_pg_class`].
391///
392/// # Returns
393///
394/// A pointer to the [`pg_sys::HeapTuple`]'s "user data", cast as a mutable pointer to `T`.  If the
395/// specified `htup` pointer is null, the null pointer is returned.
396///
397/// # Safety
398///
399/// This function cannot verify that the specified `htup` points to a valid [`pg_sys::HeapTuple`] nor
400/// that if it does, that its bytes are bitwise compatible with `T`.
401///
402/// [`FormData_pg_class`]: crate::FormData_pg_class
403#[inline]
404pub unsafe fn heap_tuple_get_struct<T>(htup: super::HeapTuple) -> *mut T {
405    if htup.is_null() {
406        std::ptr::null_mut()
407    } else {
408        unsafe {
409            // SAFETY:  The caller has told us `htop` is a valid HeapTuple
410            GETSTRUCT(htup).cast()
411        }
412    }
413}
414
415// All of this weird code is in response to Postgres having a relatively cavalier attitude about types:
416// - https://github.com/postgres/postgres/commit/1c27d16e6e5c1f463bbe1e9ece88dda811235165
417//
418// As a result, we redeclare their functions with the arguments they should have on earlier Postgres
419// and we route people to the old symbols they were using before on later ones.
420#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
421#[::pgrx_macros::pg_guard]
422unsafe extern "C-unwind" {
423    pub fn planstate_tree_walker(
424        planstate: *mut super::PlanState,
425        walker: ::core::option::Option<
426            unsafe extern "C-unwind" fn(*mut super::PlanState, *mut ::core::ffi::c_void) -> bool,
427        >,
428        context: *mut ::core::ffi::c_void,
429    ) -> bool;
430
431    pub fn query_tree_walker(
432        query: *mut super::Query,
433        walker: ::core::option::Option<
434            unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
435        >,
436        context: *mut ::core::ffi::c_void,
437        flags: ::core::ffi::c_int,
438    ) -> bool;
439
440    pub fn query_or_expression_tree_walker(
441        node: *mut super::Node,
442        walker: ::core::option::Option<
443            unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
444        >,
445        context: *mut ::core::ffi::c_void,
446        flags: ::core::ffi::c_int,
447    ) -> bool;
448
449    pub fn range_table_entry_walker(
450        rte: *mut super::RangeTblEntry,
451        walker: ::core::option::Option<
452            unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
453        >,
454        context: *mut ::core::ffi::c_void,
455        flags: ::core::ffi::c_int,
456    ) -> bool;
457
458    pub fn range_table_walker(
459        rtable: *mut super::List,
460        walker: ::core::option::Option<
461            unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
462        >,
463        context: *mut ::core::ffi::c_void,
464        flags: ::core::ffi::c_int,
465    ) -> bool;
466
467    pub fn expression_tree_walker(
468        node: *mut super::Node,
469        walker: ::core::option::Option<
470            unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
471        >,
472        context: *mut ::core::ffi::c_void,
473    ) -> bool;
474
475    pub fn raw_expression_tree_walker(
476        node: *mut super::Node,
477        walker: ::core::option::Option<
478            unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
479        >,
480        context: *mut ::core::ffi::c_void,
481    ) -> bool;
482}
483
484#[cfg(any(feature = "pg16", feature = "pg17", feature = "pg18", feature = "pg19"))]
485pub unsafe fn planstate_tree_walker(
486    planstate: *mut super::PlanState,
487    walker: ::core::option::Option<
488        unsafe extern "C-unwind" fn(*mut super::PlanState, *mut ::core::ffi::c_void) -> bool,
489    >,
490    context: *mut ::core::ffi::c_void,
491) -> bool {
492    crate::planstate_tree_walker_impl(planstate, walker, context)
493}
494
495#[cfg(any(feature = "pg16", feature = "pg17", feature = "pg18", feature = "pg19"))]
496pub unsafe fn query_tree_walker(
497    query: *mut super::Query,
498    walker: ::core::option::Option<
499        unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
500    >,
501    context: *mut ::core::ffi::c_void,
502    flags: ::core::ffi::c_int,
503) -> bool {
504    crate::query_tree_walker_impl(query, walker, context, flags)
505}
506
507#[cfg(any(feature = "pg16", feature = "pg17", feature = "pg18", feature = "pg19"))]
508pub unsafe fn query_or_expression_tree_walker(
509    node: *mut super::Node,
510    walker: ::core::option::Option<
511        unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
512    >,
513    context: *mut ::core::ffi::c_void,
514    flags: ::core::ffi::c_int,
515) -> bool {
516    crate::query_or_expression_tree_walker_impl(node, walker, context, flags)
517}
518
519#[cfg(any(feature = "pg16", feature = "pg17", feature = "pg18", feature = "pg19"))]
520pub unsafe fn expression_tree_walker(
521    node: *mut crate::Node,
522    walker: Option<unsafe extern "C-unwind" fn(*mut crate::Node, *mut ::core::ffi::c_void) -> bool>,
523    context: *mut ::core::ffi::c_void,
524) -> bool {
525    crate::expression_tree_walker_impl(node, walker, context)
526}
527
528#[cfg(any(feature = "pg16", feature = "pg17", feature = "pg18", feature = "pg19"))]
529pub unsafe fn range_table_entry_walker(
530    rte: *mut super::RangeTblEntry,
531    walker: ::core::option::Option<
532        unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
533    >,
534    context: *mut ::core::ffi::c_void,
535    flags: ::core::ffi::c_int,
536) -> bool {
537    crate::range_table_entry_walker_impl(rte, walker, context, flags)
538}
539
540#[cfg(any(feature = "pg16", feature = "pg17", feature = "pg18", feature = "pg19"))]
541pub unsafe fn range_table_walker(
542    rtable: *mut super::List,
543    walker: ::core::option::Option<
544        unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
545    >,
546    context: *mut ::core::ffi::c_void,
547    flags: ::core::ffi::c_int,
548) -> bool {
549    crate::range_table_walker_impl(rtable, walker, context, flags)
550}
551
552#[cfg(any(feature = "pg16", feature = "pg17", feature = "pg18", feature = "pg19"))]
553pub unsafe fn raw_expression_tree_walker(
554    node: *mut crate::Node,
555    walker: Option<unsafe extern "C-unwind" fn(*mut crate::Node, *mut ::core::ffi::c_void) -> bool>,
556    context: *mut ::core::ffi::c_void,
557) -> bool {
558    crate::raw_expression_tree_walker_impl(node, walker, context)
559}
560
561#[cfg(any(feature = "pg18", feature = "pg19"))]
562pub unsafe fn expression_tree_mutator(
563    node: *mut crate::Node,
564    mutator: crate::tree_mutator_callback,
565    context: *mut ::core::ffi::c_void,
566) -> *mut crate::Node {
567    crate::expression_tree_mutator_impl(node, mutator, context)
568}
569
570#[inline(always)]
571pub unsafe fn MemoryContextSwitchTo(context: crate::MemoryContext) -> crate::MemoryContext {
572    let old = crate::CurrentMemoryContext;
573
574    crate::CurrentMemoryContext = context;
575    old
576}
577
578#[allow(non_snake_case)]
579#[inline(always)]
580#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
581pub unsafe fn BufferGetPageSize(buffer: pg_sys::Buffer) -> pg_sys::Size {
582    // #define BufferGetPageSize(buffer) \
583    // ( \
584    //     AssertMacro(BufferIsValid(buffer)), \
585    //     (Size)BLCKSZ \
586    // )
587    assert!(BufferIsValid(buffer));
588    pg_sys::BLCKSZ as pg_sys::Size
589}
590
591#[allow(non_snake_case)]
592#[inline(always)]
593#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
594pub unsafe fn ItemIdGetOffset(item_id: pg_sys::ItemId) -> u32 {
595    // #define ItemIdGetOffset(itemId) \
596    // ((itemId)->lp_off)
597    (*item_id).lp_off()
598}
599
600#[allow(non_snake_case)]
601#[inline(always)]
602pub const unsafe fn PageIsValid(page: pg_sys::Page) -> bool {
603    // #define PageIsValid(page) PointerIsValid(page)
604    !page.is_null()
605}
606
607#[allow(non_snake_case)]
608#[inline(always)]
609#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
610pub unsafe fn PageIsEmpty(page: pg_sys::Page) -> bool {
611    // #define PageIsEmpty(page) \
612    // (((PageHeader) (page))->pd_lower <= SizeOfPageHeaderData)
613    const SizeOfPageHeaderData: pg_sys::Size =
614        core::mem::offset_of!(pg_sys::PageHeaderData, pd_linp);
615    let page_header = page as *mut pg_sys::PageHeaderData;
616    (*page_header).pd_lower <= SizeOfPageHeaderData as u16
617}
618
619#[allow(non_snake_case)]
620#[inline(always)]
621#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
622pub unsafe fn PageIsNew(page: pg_sys::Page) -> bool {
623    // #define PageIsNew(page) (((PageHeader) (page))->pd_upper == 0)
624    let page_header = page as *mut pg_sys::PageHeaderData;
625    (*page_header).pd_upper == 0
626}
627
628#[allow(non_snake_case)]
629#[inline(always)]
630#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
631pub unsafe fn PageGetItemId(page: pg_sys::Page, offset: pg_sys::OffsetNumber) -> pg_sys::ItemId {
632    // #define PageGetItemId(page, offsetNumber) \
633    // ((ItemId) (&((PageHeader) (page))->pd_linp[(offsetNumber) - 1]))
634    let page_header = page as *mut pg_sys::PageHeaderData;
635    (*page_header).pd_linp.as_mut_ptr().add(offset as usize - 1)
636}
637
638#[allow(non_snake_case)]
639#[inline(always)]
640#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
641pub unsafe fn PageGetContents(page: pg_sys::Page) -> *mut ::core::ffi::c_char {
642    // #define PageGetContents(page) \
643    // ((char *) (page) + MAXALIGN(SizeOfPageHeaderData))
644    const SizeOfPageHeaderData: pg_sys::Size =
645        core::mem::offset_of!(pg_sys::PageHeaderData, pd_linp);
646    page.add(pg_sys::MAXALIGN(SizeOfPageHeaderData)) as *mut ::core::ffi::c_char
647}
648
649#[allow(non_snake_case)]
650#[inline(always)]
651#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
652pub fn PageSizeIsValid(page_size: usize) -> bool {
653    // #define PageSizeIsValid(pageSize) ((pageSize) == BLCKSZ)
654    page_size == pg_sys::BLCKSZ as usize
655}
656
657#[allow(non_snake_case)]
658#[inline(always)]
659#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
660pub unsafe fn PageGetPageSize(page: pg_sys::Page) -> usize {
661    // #define PageGetPageSize(page) \
662    // ((Size) (((PageHeader) (page))->pd_pagesize_version & (uint16) 0xFF00))
663    let page_header = page as *mut pg_sys::PageHeaderData;
664    ((*page_header).pd_pagesize_version & 0xFF00) as usize
665}
666
667#[allow(non_snake_case)]
668#[inline(always)]
669#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
670pub unsafe fn PageGetPageLayoutVersion(page: pg_sys::Page) -> ::core::ffi::c_char {
671    // #define PageGetPageLayoutVersion(page) \
672    // (((PageHeader) (page))->pd_pagesize_version & 0x00FF)
673    let page_header = page as *mut pg_sys::PageHeaderData;
674    ((*page_header).pd_pagesize_version & 0x00FF) as ::core::ffi::c_char
675}
676
677#[allow(non_snake_case)]
678#[inline(always)]
679#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
680pub unsafe fn PageSetPageSizeAndVersion(page: pg_sys::Page, size: u16, version: u8) {
681    // #define PageSetPageSizeAndVersion(page, size, version) \
682    // ((PageHeader) (page))->pd_pagesize_version = (size) | (version)
683    let page_header = page as *mut pg_sys::PageHeaderData;
684    (*page_header).pd_pagesize_version = size | (version as u16);
685}
686
687#[allow(non_snake_case)]
688#[inline(always)]
689#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
690pub unsafe fn PageGetSpecialSize(page: pg_sys::Page) -> u16 {
691    // #define PageGetSpecialSize(page) \
692    // ((uint16) (PageGetPageSize(page) - ((PageHeader)(page))->pd_special))
693    let page_header = page as *mut pg_sys::PageHeaderData;
694    PageGetPageSize(page) as u16 - (*page_header).pd_special
695}
696
697/// line pointer(s) do not count as part of header
698pub const unsafe fn SizeOfPageHeaderData() -> usize {
699    /*
700       #define SizeOfPageHeaderData (offsetof(PageHeaderData, pd_linp))
701    */
702    offset_of!(pg_sys::PageHeaderData, pd_linp)
703}
704
705/// Using assertions, validate that the page special pointer is OK.
706///
707/// This is intended to catch use of the pointer before page initialization.
708/// It is implemented as a function due to the limitations of the MSVC
709/// compiler, which choked on doing all these tests within another macro.  We
710/// return true so that AssertMacro() can be used while still getting the
711/// specifics from the macro failure within this function.
712#[allow(non_snake_case)]
713#[inline(always)]
714pub const unsafe fn PageValidateSpecialPointer(page: pg_sys::Page) -> bool {
715    // static inline bool
716    // PageValidateSpecialPointer(Page page)
717    // {
718    //     Assert(PageIsValid(page));
719    //     Assert(((PageHeader) (page))->pd_special <= BLCKSZ);
720    //     Assert(((PageHeader) (page))->pd_special >= SizeOfPageHeaderData);
721    //
722    //     return true;
723    // }
724    assert!(PageIsValid(page));
725    let page = page as *mut pg_sys::PageHeaderData;
726    assert!((*page).pd_special <= BLCKSZ as _);
727    assert!((*page).pd_special >= SizeOfPageHeaderData() as _);
728    true
729}
730
731#[allow(non_snake_case)]
732#[inline(always)]
733#[cfg(any(
734    feature = "pg13",
735    feature = "pg14",
736    feature = "pg15",
737    feature = "pg18",
738    feature = "pg19"
739))]
740pub unsafe fn PageGetSpecialPointer(page: pg_sys::Page) -> *mut ::core::ffi::c_char {
741    /*
742    #define PageGetSpecialPointer(page) \
743    ( \
744        PageValidateSpecialPointer(page), \
745        ((page) + ((PageHeader) (page))->pd_special) \
746    )
747    */
748
749    assert!(PageValidateSpecialPointer(page));
750
751    let page_header = page as *mut pg_sys::PageHeaderData;
752    page.add((*page_header).pd_special as usize) as *mut ::core::ffi::c_char
753}
754
755#[allow(non_snake_case)]
756#[inline(always)]
757#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
758pub unsafe fn PageGetItem(page: pg_sys::Page, item_id: pg_sys::ItemId) -> *mut ::core::ffi::c_char {
759    // #define PageGetItem(page, itemId) \
760    // (((char *)(page)) + ItemIdGetOffset(itemId))
761    page.add(ItemIdGetOffset(item_id) as usize)
762}
763
764#[allow(non_snake_case)]
765#[inline(always)]
766#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
767pub unsafe fn PageGetMaxOffsetNumber(page: pg_sys::Page) -> pg_sys::OffsetNumber {
768    // #define PageGetMaxOffsetNumber(page) \
769    // (((PageHeader) (page))->pd_lower <= SizeOfPageHeaderData ? 0 : \
770    // ((((PageHeader) (page))->pd_lower - SizeOfPageHeaderData) / sizeof(ItemIdData)))
771    const SizeOfPageHeaderData: pg_sys::Size =
772        core::mem::offset_of!(pg_sys::PageHeaderData, pd_linp);
773    let page_header = page as *mut pg_sys::PageHeaderData;
774    if (*page_header).pd_lower <= SizeOfPageHeaderData as u16 {
775        0
776    } else {
777        ((*page_header).pd_lower - SizeOfPageHeaderData as u16)
778            / std::mem::size_of::<pg_sys::ItemIdData>() as u16
779    }
780}
781
782#[allow(non_snake_case)]
783#[inline(always)]
784#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
785pub unsafe fn BufferIsValid(buffer: pg_sys::Buffer) -> bool {
786    // static inline bool
787    // BufferIsValid(Buffer bufnum)
788    // {
789    //     Assert(bufnum <= NBuffers);
790    //     Assert(bufnum >= -NLocBuffer);
791
792    //     return bufnum != InvalidBuffer;
793    // }
794    assert!(buffer <= pg_sys::NBuffers);
795    assert!(buffer >= -pg_sys::NLocBuffer);
796    buffer != pg_sys::InvalidBuffer as pg_sys::Buffer
797}