@@ -27,23 +27,10 @@ use crate::rel::PgRelation;
2727use crate :: { PgBox , PgMemoryContexts } ;
2828
2929use core:: marker:: PhantomData ;
30- use core:: { iter , mem, ptr, slice } ;
30+ use core:: { mem, ptr} ;
3131use std:: ffi:: { CStr , CString } ;
3232use std:: ptr:: NonNull ;
3333
34- struct ReadOnly < T > ( T ) ;
35-
36- impl < T > ReadOnly < T > {
37- unsafe fn refer_to ( & self ) -> & T {
38- & self . 0
39- }
40- }
41-
42- unsafe impl < T > Sync for ReadOnly < T > { }
43-
44- static VIRTUAL_ARGUMENT : ReadOnly < pg_sys:: NullableDatum > =
45- ReadOnly ( pg_sys:: NullableDatum { value : pg_sys:: Datum :: null ( ) , isnull : true } ) ;
46-
4734/// How to pass a value from Postgres to Rust
4835///
4936/// This bound is necessary to distinguish things which can be passed into a `#[pg_extern] fn`.
@@ -288,7 +275,7 @@ where
288275 } ;
289276 unsafe {
290277 let ptr = ptr:: NonNull :: new_unchecked ( ptr) ;
291- T :: borrow_unchecked ( ptr)
278+ T :: borrow_arg_unchecked ( ptr, |ptr| arg . 0 . register_detoasted_arg ( ptr ) )
292279 }
293280 }
294281
@@ -299,7 +286,11 @@ where
299286 } ) ;
300287 match ( arg. is_null ( ) , ptr) {
301288 ( true , _) | ( false , None ) => Nullable :: Null ,
302- ( false , Some ( ptr) ) => unsafe { Nullable :: Valid ( T :: borrow_unchecked ( ptr) ) } ,
289+ ( false , Some ( ptr) ) => unsafe {
290+ Nullable :: Valid ( T :: borrow_arg_unchecked ( ptr, |ptr| {
291+ arg. 0 . register_detoasted_arg ( ptr)
292+ } ) )
293+ } ,
303294 }
304295 }
305296}
@@ -599,8 +590,26 @@ where
599590
600591type FcInfoData = pg_sys:: FunctionCallInfoBaseData ;
601592
602- #[ derive( Clone ) ]
603- pub struct FcInfo < ' fcx > ( pgrx_pg_sys:: FunctionCallInfo , PhantomData < & ' fcx mut FcInfoData > ) ;
593+ /// A uniquely owned PostgreSQL function call frame.
594+ ///
595+ /// Argument decoding may register temporary detoast allocations here, so this type is intentionally
596+ /// not cloneable.
597+ pub struct FcInfo < ' fcx > (
598+ pgrx_pg_sys:: FunctionCallInfo ,
599+ PhantomData < & ' fcx mut FcInfoData > ,
600+ Vec < NonNull < u8 > > ,
601+ pg_sys:: MemoryContext ,
602+ ) ;
603+
604+ impl Drop for FcInfo < ' _ > {
605+ fn drop ( & mut self ) {
606+ for allocation in self . 2 . drain ( ..) {
607+ // SAFETY: `BorrowDatum::borrow_arg_unchecked` requires registered pointers to be
608+ // PostgreSQL allocations that can be released after the function result is boxed.
609+ unsafe { pg_sys:: pfree ( allocation. as_ptr ( ) . cast ( ) ) }
610+ }
611+ }
612+ }
604613
605614// when talking about this, there's the lifetime for setreturningfunction, and then there's the current context's lifetime.
606615// Potentially <'srf, 'curr, 'ret: 'curr + 'srf> -> <'ret> but don't start with that.
@@ -620,7 +629,19 @@ impl<'fcx> FcInfo<'fcx> {
620629 #[ inline]
621630 pub unsafe fn from_ptr ( fcinfo : pg_sys:: FunctionCallInfo ) -> FcInfo < ' fcx > {
622631 let _nullptr_check = NonNull :: new ( fcinfo) . expect ( "fcinfo pointer must be non-null" ) ;
623- Self ( fcinfo, PhantomData )
632+ Self ( fcinfo, PhantomData , Vec :: new ( ) , unsafe { pg_sys:: CurrentMemoryContext } )
633+ }
634+
635+ fn register_detoasted_arg ( & mut self , ptr : NonNull < u8 > ) {
636+ // Set-returning functions unbox their arguments in a multi-call context so an iterator
637+ // may retain borrows across calls. That context owns its detoast allocations and releases
638+ // them when the SRF finishes. Ordinary functions unbox in the entry context, where we can
639+ // release fresh detoast copies as soon as their result has been boxed.
640+ if unsafe { pg_sys:: CurrentMemoryContext } != self . 3 {
641+ return ;
642+ }
643+
644+ self . 2 . push ( ptr) ;
624645 }
625646 /// Retrieve the arguments to this function call as a slice of [`pgrx_pg_sys::NullableDatum`]
626647 #[ inline]
@@ -830,14 +851,18 @@ impl<'fcx> FcInfo<'fcx> {
830851 }
831852 }
832853
854+ /// Create a decoder for this function call's arguments.
855+ ///
856+ /// The decoder borrows the call frame exclusively so temporary argument allocations have one
857+ /// unambiguous owner.
833858 #[ inline]
834- pub fn args < ' arg > ( & ' arg self ) -> Args < ' arg , ' fcx > {
835- Args { iter : self . raw_args ( ) . iter ( ) . enumerate ( ) , fcinfo : self }
859+ pub fn args < ' arg > ( & ' arg mut self ) -> Args < ' arg , ' fcx > {
860+ Args { next : 0 , fcinfo : self }
836861 }
837862}
838863
839864// TODO: rebadge this as AnyElement
840- pub struct Arg < ' a , ' fcx > ( & ' a FcInfo < ' fcx > , usize , & ' a pg_sys:: NullableDatum ) ;
865+ pub struct Arg < ' a , ' fcx > ( & ' a mut FcInfo < ' fcx > , usize , pg_sys:: NullableDatum ) ;
841866
842867impl < ' a , ' fcx > Arg < ' a , ' fcx > {
843868 /// # Performance note
@@ -880,23 +905,30 @@ impl<'a, 'fcx> Arg<'a, 'fcx> {
880905 }
881906}
882907
908+ /// A lending-style decoder for PostgreSQL function arguments.
909+ ///
910+ /// This is deliberately not an [`Iterator`]: each decoded [`Arg`] temporarily borrows the call
911+ /// frame's detoast cleanup state.
883912pub struct Args < ' a , ' fcx > {
884- iter : iter:: Enumerate < slice:: Iter < ' a , pg_sys:: NullableDatum > > ,
885- fcinfo : & ' a FcInfo < ' fcx > ,
886- }
887-
888- impl < ' a , ' fcx > Iterator for Args < ' a , ' fcx > {
889- type Item = Arg < ' a , ' fcx > ;
890-
891- fn next ( & mut self ) -> Option < Self :: Item > {
892- self . iter . next ( ) . map ( |( a, b) | Arg ( self . fcinfo , a, b) )
893- }
913+ next : usize ,
914+ fcinfo : & ' a mut FcInfo < ' fcx > ,
894915}
895916
896917impl < ' a , ' fcx > Args < ' a , ' fcx > {
897918 // generate an argument for use by virtual args
898- fn synthesize_virtual_arg ( & self ) -> Arg < ' a , ' fcx > {
899- Arg ( self . fcinfo , usize:: MAX , unsafe { VIRTUAL_ARGUMENT . refer_to ( ) } )
919+ fn synthesize_virtual_arg ( & mut self ) -> Arg < ' _ , ' fcx > {
920+ Arg (
921+ self . fcinfo ,
922+ usize:: MAX ,
923+ pg_sys:: NullableDatum { value : pg_sys:: Datum :: null ( ) , isnull : true } ,
924+ )
925+ }
926+
927+ fn next_raw ( & mut self ) -> Option < Arg < ' _ , ' fcx > > {
928+ let index = self . next ;
929+ let datum = self . fcinfo . raw_args ( ) . get ( index) . copied ( ) ?;
930+ self . next += 1 ;
931+ Some ( Arg ( self . fcinfo , index, datum) )
900932 }
901933
902934 /// # Safety
@@ -910,7 +942,7 @@ impl<'a, 'fcx> Args<'a, 'fcx> {
910942 unsafe { Some ( T :: unbox_arg_unchecked ( self . synthesize_virtual_arg ( ) ) ) }
911943 } else {
912944 // SAFETY: caller upholds
913- unsafe { self . next ( ) . map ( |next| T :: unbox_arg_unchecked ( next) ) }
945+ unsafe { self . next_raw ( ) . map ( |next| T :: unbox_arg_unchecked ( next) ) }
914946 }
915947 }
916948
@@ -923,7 +955,7 @@ impl<'a, 'fcx> Args<'a, 'fcx> {
923955 unsafe { Some ( T :: unbox_nullable_arg ( self . synthesize_virtual_arg ( ) ) ) }
924956 } else {
925957 // SAFETY: caller upholds
926- unsafe { self . next ( ) . map ( |next| T :: unbox_nullable_arg ( next) ) }
958+ unsafe { self . next_raw ( ) . map ( |next| T :: unbox_nullable_arg ( next) ) }
927959 }
928960 }
929961}
0 commit comments