From 3d00e172033f72e97095d756b6bb9aa5d2a2524b Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Mon, 17 Mar 2025 19:38:52 +0300 Subject: [PATCH 1/5] Added mapping for xid --- pgrx-bindgen/src/build.rs | 6 +- pgrx-pg-sys/src/port.rs | 10 +-- pgrx-pg-sys/src/submodules/mod.rs | 2 + pgrx-pg-sys/src/submodules/transaction_id.rs | 74 ++++++++++++++++++++ pgrx-tests/src/tests/roundtrip_tests.rs | 1 + pgrx-tests/src/tests/xid64_tests.rs | 2 +- pgrx/src/callconv.rs | 4 +- pgrx/src/datum/from.rs | 15 ++++ pgrx/src/datum/into.rs | 16 +++++ pgrx/src/xid.rs | 12 ++-- 10 files changed, 128 insertions(+), 14 deletions(-) create mode 100644 pgrx-pg-sys/src/submodules/transaction_id.rs diff --git a/pgrx-bindgen/src/build.rs b/pgrx-bindgen/src/build.rs index 34b2d1fe3f..bec83374e1 100644 --- a/pgrx-bindgen/src/build.rs +++ b/pgrx-bindgen/src/build.rs @@ -24,7 +24,7 @@ use std::process::{Command, Output}; use std::rc::Rc; use syn::{Item, ItemConst}; -const BLOCKLISTED_TYPES: [&str; 3] = ["Datum", "NullableDatum", "Oid"]; +const BLOCKLISTED_TYPES: [&str; 4] = ["Datum", "NullableDatum", "Oid", "TransactionId"]; // These postgres versions were effectively "yanked" by the community, even tho they still exist // in the wild. pgrx will refuse to compile against them @@ -348,7 +348,7 @@ fn generate_bindings( &bindings_file, quote! { use crate as pg_sys; - use crate::{Datum, Oid, PgNode}; + use crate::{Datum, MultiXactId, Oid, PgNode, TransactionId}; }, is_for_release, ) @@ -864,6 +864,8 @@ pub const {module}_{variant}: {ty} = {value};"#, fn add_blocklists(bind: bindgen::Builder) -> bindgen::Builder { bind.blocklist_type("Datum") // manually wrapping datum for correctness .blocklist_type("Oid") // "Oid" is not just any u32 + .blocklist_type("TransactionId") // "TransactionId" is not just any u32 + .blocklist_type("MultiXactId") // it's an alias of "TransactionId" .blocklist_var("CONFIGURE_ARGS") // configuration during build is hopefully irrelevant .blocklist_var("_*(?:HAVE|have)_.*") // header tracking metadata .blocklist_var("_[A-Z_]+_H") // more header metadata diff --git a/pgrx-pg-sys/src/port.rs b/pgrx-pg-sys/src/port.rs index e181d5b030..1858a20e29 100644 --- a/pgrx-pg-sys/src/port.rs +++ b/pgrx-pg-sys/src/port.rs @@ -10,13 +10,13 @@ pub const MaxOffsetNumber: super::OffsetNumber = (super::BLCKSZ as usize / std::mem::size_of::()) as super::OffsetNumber; pub const InvalidBlockNumber: u32 = 0xFFFF_FFFF as crate::BlockNumber; pub const VARHDRSZ: usize = std::mem::size_of::(); -pub const InvalidTransactionId: super::TransactionId = 0 as super::TransactionId; pub const InvalidCommandId: super::CommandId = (!(0 as super::CommandId)) as super::CommandId; pub const FirstCommandId: super::CommandId = 0 as super::CommandId; -pub const BootstrapTransactionId: super::TransactionId = 1 as super::TransactionId; -pub const FrozenTransactionId: super::TransactionId = 2 as super::TransactionId; -pub const FirstNormalTransactionId: super::TransactionId = 3 as super::TransactionId; -pub const MaxTransactionId: super::TransactionId = 0xFFFF_FFFF as super::TransactionId; +pub const InvalidTransactionId: crate::TransactionId = crate::TransactionId::INVALID; +pub const BootstrapTransactionId: crate::TransactionId = crate::TransactionId::BOOTSTRAP; +pub const FrozenTransactionId: crate::TransactionId = crate::TransactionId::FROZEN; +pub const FirstNormalTransactionId: crate::TransactionId = crate::TransactionId::FIRST_NORMAL; +pub const MaxTransactionId: crate::TransactionId = crate::TransactionId::MAX; /// Given a valid HeapTuple pointer, return address of the user data /// diff --git a/pgrx-pg-sys/src/submodules/mod.rs b/pgrx-pg-sys/src/submodules/mod.rs index d3c99725b4..5e3362f014 100644 --- a/pgrx-pg-sys/src/submodules/mod.rs +++ b/pgrx-pg-sys/src/submodules/mod.rs @@ -8,6 +8,7 @@ //LICENSE //LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file. pub mod datum; +pub mod transaction_id; #[macro_use] pub mod elog; pub mod cmp; @@ -27,6 +28,7 @@ pub mod utils; mod sql_translatable; pub use datum::Datum; +pub use transaction_id::{MultiXactId, TransactionId}; pub use htup::*; pub use oids::*; diff --git a/pgrx-pg-sys/src/submodules/transaction_id.rs b/pgrx-pg-sys/src/submodules/transaction_id.rs new file mode 100644 index 0000000000..bfb970546f --- /dev/null +++ b/pgrx-pg-sys/src/submodules/transaction_id.rs @@ -0,0 +1,74 @@ +//LICENSE Portions Copyright 2019-2021 ZomboDB, LLC. +//LICENSE +//LICENSE Portions Copyright 2021-2023 Technology Concepts & Design, Inc. +//LICENSE +//LICENSE Portions Copyright 2023-2023 PgCentral Foundation, Inc. +//LICENSE +//LICENSE All rights reserved. +//LICENSE +//LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file. +use pgrx_sql_entity_graph::metadata::{ + ArgumentError, Returns, ReturnsError, SqlMapping, SqlTranslatable, +}; + +pub type MultiXactId = TransactionId; + +/// An `xid` type from PostgreSQL +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(serde::Deserialize, serde::Serialize)] +pub struct TransactionId(u32); + +impl TransactionId { + pub const INVALID: Self = Self::from_u32(0); + pub const BOOTSTRAP: Self = Self::from_u32(1); + pub const FROZEN: Self = Self::from_u32(2); + pub const FIRST_NORMAL: Self = Self::from_u32(3); + pub const MAX: Self = Self::from_u32(u32::MAX); + + #[inline] + pub const fn from_u32(xid: u32) -> Self { + Self(xid) + } + + #[inline] + pub const fn to_u32(self) -> u32 { + self.0 + } +} + +impl Default for TransactionId { + fn default() -> Self { + Self::INVALID + } +} + +impl From for TransactionId { + #[inline] + fn from(xid: u32) -> Self { + Self::from_u32(xid) + } +} + +impl From for u32 { + #[inline] + fn from(xid: TransactionId) -> Self { + xid.to_u32() + } +} + +impl From for crate::Datum { + fn from(xid: TransactionId) -> Self { + xid.to_u32().into() + } +} + +unsafe impl SqlTranslatable for TransactionId { + fn argument_sql() -> Result { + Ok(SqlMapping::literal("xid")) + } + + fn return_sql() -> Result { + Ok(Returns::One(SqlMapping::literal("xid"))) + } +} diff --git a/pgrx-tests/src/tests/roundtrip_tests.rs b/pgrx-tests/src/tests/roundtrip_tests.rs index 264708675d..8dce10018e 100644 --- a/pgrx-tests/src/tests/roundtrip_tests.rs +++ b/pgrx-tests/src/tests/roundtrip_tests.rs @@ -125,6 +125,7 @@ mod tests { roundtrip!(rt_point, test_rt_point, pg_sys::Point, pg_sys::Point { x: 1.0, y: 2.0 }); roundtrip!(rt_string, test_rt_string, String, String::from("string")); roundtrip!(rt_oid, test_rt_oid, pg_sys::Oid, pg_sys::Oid::from(BuiltinOid::ANYOID)); + roundtrip!(rt_xid, test_rt_xid, pg_sys::TransactionId, pg_sys::TransactionId::FIRST_NORMAL); roundtrip!(rt_i16, test_rt_i16, i16, i16::MAX); roundtrip!(rt_f64, test_rt_f64, f64, f64::MAX); roundtrip!( diff --git a/pgrx-tests/src/tests/xid64_tests.rs b/pgrx-tests/src/tests/xid64_tests.rs index 3783c263e8..670a7e859c 100644 --- a/pgrx-tests/src/tests/xid64_tests.rs +++ b/pgrx-tests/src/tests/xid64_tests.rs @@ -21,7 +21,7 @@ mod tests { #[pg_test] fn test_convert_xid_to_u64() { - let xid = xid_to_64bit(32768); + let xid = xid_to_64bit(32768.into()); assert_eq!(xid, 32768) } } diff --git a/pgrx/src/callconv.rs b/pgrx/src/callconv.rs index 46733e0147..fb41904d7c 100644 --- a/pgrx/src/callconv.rs +++ b/pgrx/src/callconv.rs @@ -265,7 +265,7 @@ argue_from_datum! { 'fcx; i8, i16, i32, i64, f32, f64, bool, char, String, Vec, char, Json, JsonB, Inet, Uuid, AnyNumeric, AnyArray, AnyElement, Internal, Date, Interval, Time, TimeWithTimeZone, Timestamp, TimestampWithTimeZone, - pg_sys::BOX, pg_sys::ItemPointerData, pg_sys::Oid, pg_sys::Point + pg_sys::BOX, pg_sys::ItemPointerData, pg_sys::Oid, pg_sys::Point, pg_sys::TransactionId } unsafe impl BoxRet for Numeric { diff --git a/pgrx/src/datum/from.rs b/pgrx/src/datum/from.rs index a327bcdfae..69a8793e3b 100644 --- a/pgrx/src/datum/from.rs +++ b/pgrx/src/datum/from.rs @@ -209,6 +209,21 @@ impl FromDatum for pg_sys::Oid { } } +impl FromDatum for pg_sys::TransactionId { + #[inline] + unsafe fn from_polymorphic_datum( + datum: pg_sys::Datum, + is_null: bool, + _typoid: pg_sys::Oid, + ) -> Option { + if is_null { + None + } else { + datum.value().try_into().ok().map(Self::from_u32) + } + } +} + /// for bool impl FromDatum for bool { #[inline] diff --git a/pgrx/src/datum/into.rs b/pgrx/src/datum/into.rs index 50f69111df..1d0010d799 100644 --- a/pgrx/src/datum/into.rs +++ b/pgrx/src/datum/into.rs @@ -236,6 +236,22 @@ impl IntoDatum for pg_sys::Oid { } } +impl IntoDatum for pg_sys::TransactionId { + #[inline] + fn into_datum(self) -> Option { + if self == Self::INVALID { + None + } else { + Some(self.into()) + } + } + + #[inline] + fn type_oid() -> pg_sys::Oid { + pg_sys::XIDOID + } +} + impl IntoDatum for PgOid { #[inline] fn into_datum(self) -> Option { diff --git a/pgrx/src/xid.rs b/pgrx/src/xid.rs index ae5646f74f..8d42afcafc 100644 --- a/pgrx/src/xid.rs +++ b/pgrx/src/xid.rs @@ -16,14 +16,18 @@ pub fn xid_to_64bit(xid: pg_sys::TransactionId) -> u64 { let last_xid = full_xid.value as u32; let epoch = (full_xid.value >> 32) as u32; - convert_xid_common(xid, last_xid, epoch) + convert_xid_common(xid, last_xid.into(), epoch) } #[inline] -fn convert_xid_common(xid: pg_sys::TransactionId, last_xid: u32, epoch: u32) -> u64 { +fn convert_xid_common( + xid: pg_sys::TransactionId, + last_xid: pgrx_pg_sys::TransactionId, + epoch: u32, +) -> u64 { /* return special xid's as-is */ if !pg_sys::TransactionIdIsNormal(xid) { - return xid as u64; + return xid.to_u32() as u64; } /* xid can be on either side when near wrap-around */ @@ -34,5 +38,5 @@ fn convert_xid_common(xid: pg_sys::TransactionId, last_xid: u32, epoch: u32) -> epoch += 1; } - (epoch << 32) | xid as u64 + (epoch << 32) | xid.to_u32() as u64 } From 26eb3ca1af02b579e09d26cbc267552ef535a400 Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Sun, 30 Mar 2025 01:18:31 +0300 Subject: [PATCH 2/5] Made xid repr transparent --- pgrx-pg-sys/src/submodules/transaction_id.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgrx-pg-sys/src/submodules/transaction_id.rs b/pgrx-pg-sys/src/submodules/transaction_id.rs index bfb970546f..321ea632a9 100644 --- a/pgrx-pg-sys/src/submodules/transaction_id.rs +++ b/pgrx-pg-sys/src/submodules/transaction_id.rs @@ -14,7 +14,7 @@ use pgrx_sql_entity_graph::metadata::{ pub type MultiXactId = TransactionId; /// An `xid` type from PostgreSQL -#[repr(C)] +#[repr(transparent)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(serde::Deserialize, serde::Serialize)] pub struct TransactionId(u32); From fc92cf074ff9154d848ce8161e7c12c2d20a0407 Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Mon, 31 Mar 2025 17:34:06 +0300 Subject: [PATCH 3/5] Implemented Display --- pgrx-pg-sys/src/submodules/transaction_id.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pgrx-pg-sys/src/submodules/transaction_id.rs b/pgrx-pg-sys/src/submodules/transaction_id.rs index 321ea632a9..01fc313a2f 100644 --- a/pgrx-pg-sys/src/submodules/transaction_id.rs +++ b/pgrx-pg-sys/src/submodules/transaction_id.rs @@ -10,12 +10,13 @@ use pgrx_sql_entity_graph::metadata::{ ArgumentError, Returns, ReturnsError, SqlMapping, SqlTranslatable, }; +use std::fmt::{Debug, Display, Formatter}; pub type MultiXactId = TransactionId; /// An `xid` type from PostgreSQL #[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(serde::Deserialize, serde::Serialize)] pub struct TransactionId(u32); @@ -72,3 +73,15 @@ unsafe impl SqlTranslatable for TransactionId { Ok(Returns::One(SqlMapping::literal("xid"))) } } + +impl Debug for TransactionId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&self.0, f) + } +} + +impl Display for TransactionId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.0, f) + } +} From 6d04049c6c62b1a242ce9aa0e48d7fbeb51eba85 Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Mon, 31 Mar 2025 18:20:42 +0300 Subject: [PATCH 4/5] Renamed const into and from methods --- pgrx-pg-sys/src/submodules/transaction_id.rs | 22 +++++++++----------- pgrx/src/datum/from.rs | 2 +- pgrx/src/xid.rs | 4 ++-- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/pgrx-pg-sys/src/submodules/transaction_id.rs b/pgrx-pg-sys/src/submodules/transaction_id.rs index 01fc313a2f..b251cbb52b 100644 --- a/pgrx-pg-sys/src/submodules/transaction_id.rs +++ b/pgrx-pg-sys/src/submodules/transaction_id.rs @@ -21,19 +21,17 @@ pub type MultiXactId = TransactionId; pub struct TransactionId(u32); impl TransactionId { - pub const INVALID: Self = Self::from_u32(0); - pub const BOOTSTRAP: Self = Self::from_u32(1); - pub const FROZEN: Self = Self::from_u32(2); - pub const FIRST_NORMAL: Self = Self::from_u32(3); - pub const MAX: Self = Self::from_u32(u32::MAX); + pub const INVALID: Self = Self(0); + pub const BOOTSTRAP: Self = Self(1); + pub const FROZEN: Self = Self(2); + pub const FIRST_NORMAL: Self = Self(3); + pub const MAX: Self = Self(u32::MAX); - #[inline] - pub const fn from_u32(xid: u32) -> Self { + pub const fn from_inner(xid: u32) -> Self { Self(xid) } - #[inline] - pub const fn to_u32(self) -> u32 { + pub const fn into_inner(self) -> u32 { self.0 } } @@ -47,20 +45,20 @@ impl Default for TransactionId { impl From for TransactionId { #[inline] fn from(xid: u32) -> Self { - Self::from_u32(xid) + Self::from_inner(xid) } } impl From for u32 { #[inline] fn from(xid: TransactionId) -> Self { - xid.to_u32() + xid.into_inner() } } impl From for crate::Datum { fn from(xid: TransactionId) -> Self { - xid.to_u32().into() + xid.into_inner().into() } } diff --git a/pgrx/src/datum/from.rs b/pgrx/src/datum/from.rs index 69a8793e3b..ac995541fa 100644 --- a/pgrx/src/datum/from.rs +++ b/pgrx/src/datum/from.rs @@ -219,7 +219,7 @@ impl FromDatum for pg_sys::TransactionId { if is_null { None } else { - datum.value().try_into().ok().map(Self::from_u32) + datum.value().try_into().ok().map(Self::from_inner) } } } diff --git a/pgrx/src/xid.rs b/pgrx/src/xid.rs index 8d42afcafc..5557995a76 100644 --- a/pgrx/src/xid.rs +++ b/pgrx/src/xid.rs @@ -27,7 +27,7 @@ fn convert_xid_common( ) -> u64 { /* return special xid's as-is */ if !pg_sys::TransactionIdIsNormal(xid) { - return xid.to_u32() as u64; + return xid.into_inner() as u64; } /* xid can be on either side when near wrap-around */ @@ -38,5 +38,5 @@ fn convert_xid_common( epoch += 1; } - (epoch << 32) | xid.to_u32() as u64 + (epoch << 32) | xid.into_inner() as u64 } From db0ad1b0928df8613df95ddc298b380dd71ce0cf Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Mon, 31 Mar 2025 18:28:56 +0300 Subject: [PATCH 5/5] Prettier results --- pgrx-pg-sys/src/submodules/transaction_id.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pgrx-pg-sys/src/submodules/transaction_id.rs b/pgrx-pg-sys/src/submodules/transaction_id.rs index b251cbb52b..2cc1a08793 100644 --- a/pgrx-pg-sys/src/submodules/transaction_id.rs +++ b/pgrx-pg-sys/src/submodules/transaction_id.rs @@ -10,7 +10,7 @@ use pgrx_sql_entity_graph::metadata::{ ArgumentError, Returns, ReturnsError, SqlMapping, SqlTranslatable, }; -use std::fmt::{Debug, Display, Formatter}; +use std::fmt::{self, Debug, Display, Formatter}; pub type MultiXactId = TransactionId; @@ -73,13 +73,13 @@ unsafe impl SqlTranslatable for TransactionId { } impl Debug for TransactionId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { Debug::fmt(&self.0, f) } } impl Display for TransactionId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { Display::fmt(&self.0, f) } }