pub fn register_xact_callback<F>(which_event: PgXactCallbackEvent, f: F) -> XactCallbackReceipt
where
    F: FnOnce() + std::panic::UnwindSafe + std::panic::RefUnwindSafe + 'static,
{
    // our map of xact callbacks.  It starts as None and gets initialized below in maybe_initialize()
    static mut XACT_HOOKS: Option<CallbackMap> = None;

    // internal function that we register as an XactCallback
    unsafe extern "C" fn callback(event: pg_sys::XactEvent, _arg: *mut ::std::os::raw::c_void) {
        let which_event = PgXactCallbackEvent::translate_pg_event(event);

        let hooks = match which_event {
            // pgx's XactCallbacks are per-transaction, so when the transaction is over
            // (that's either Commit or Abort, which are mutually exclusive), we replace our
            // const XACT_HOOKS with a new, empty Map so that subsequent transactions won't accidentally run
            // these hooks again.
            //
            // Note that we still run any hooks that are registered for these events in this xact
            PgXactCallbackEvent::Commit
            | PgXactCallbackEvent::Abort
            | PgXactCallbackEvent::ParallelCommit
            | PgXactCallbackEvent::ParallelAbort => XACT_HOOKS
                .replace(HashMap::new())
                .expect("XACT_HOOKS was None during Commit/Abort")
                .remove(&which_event),

            // not in a transaction-end event, so just borrow our map
            _ => XACT_HOOKS
                .as_mut()
                .expect("XACT_HOOKS was None")
                .remove(&which_event),
        };

        // if we have a vec of hooks for this event they're consumed here and executed
        // in the order they were registered
        if let Some(hooks) = hooks {
            for hook in hooks.into_iter() {
                // TODO:  do we need to catch panics and do something with them?  They'll cause
                //  the Postgres backend to Abort() if we're handling XactEvent::Commit/Abort events

                // effectively 'take' the hook from the internal RefCell
                if let Some(hook) = hook.replace(None) {
                    // and execute it under guard for proper panic/elog(ERROR) handling
                    crate::guard::guard(hook.0);
                }
            }
        }
    }

    // internal function to manage initialization of our transaction callback
    fn maybe_initialize<'a>() -> &'a mut CallbackMap {
        unsafe {
            // if this is our first time here since the Postgres backend started, XACT_HOOKS will be None
            if XACT_HOOKS.is_none() {
                // so lets swap it out with a new HashMap, which will live for the duration of the backend
                XACT_HOOKS.replace(HashMap::new());

                // and register our single callback function (internally defined above)
                pg_sys::RegisterXactCallback(Some(callback), std::ptr::null_mut());
            }

            XACT_HOOKS
                .as_mut()
                .expect("XACT_HOOKS was None during maybe_initialize") // this should never happen
        }
    }

    // get a mutable reference to XACT_HOOKS
    let hooks = maybe_initialize();

    // wrap the user-provided closure as an optional, reference counted cell
    let wrapped_func = Rc::new(RefCell::new(Some(XactCallbackWrapper(Box::new(f)))));

    // find (or create) the map Entry for the specified event and add our wrapped hook to it
    let entry = hooks.entry(which_event).or_default();
    entry.push(Rc::clone(&wrapped_func));

    // give the user the ability to unregister
    XactCallbackReceipt(wrapped_func)
}
/// Register a closure to be called during one of the `PgXactCallbackEvent` events.  Multiple
/// closures can be registered per event (one at a time), and they are called in the order in which
/// they were registered.
///
/// Registered callbacks only remain registered for the life of a single transaction.  Registration
/// of permanet callbacks should be done through the unsafe `pg_sys::RegisterXactCallback()` function.
///
///
/// ## Examples
///
/// Register a number of events for pre-commit and commit:
///
/// ```rust,no_run
/// use pgx::*;
///
/// register_xact_callback(PgXactCallbackEvent::PreCommit, || info!("pre-commit #1"));
/// register_xact_callback(PgXactCallbackEvent::PreCommit, || info!("pre-commit #2"));
/// register_xact_callback(PgXactCallbackEvent::PreCommit, || info!("pre-commit #3"));
/// register_xact_callback(PgXactCallbackEvent::Commit, || info!("called after commit"));
/// ```
///
/// Register an event, do some work, and then decide the callback isn't actually necessary anymore:
///
/// ```rust,no_run
/// use pgx::*;
///
/// // ... do some initialization work ...
///
/// let receipt = register_xact_callback(PgXactCallbackEvent::Abort, || { /* do cleanup if xact aborts */});
///
/// // ... do work that might abort the transaction ...
///
/// // if we got here, the transaction did not abort, so we no longer need to care about cleanup
/// receipt.unregister_callback();
/// ```
///
/// ## Safety
///
/// Any kind of Rust `panic!()` or Postgres `ereport(ERROR)` while executing a `PgXactCallbackEvent::Commit`
/// or `PgXactCallbackEvent::Abort` event will immediately cause the Postgres backend to abort and
/// the entire cluster to restart.
///
/// As the Postgres internal documentation says:  
///
/// At transaction end, the callback occurs post-commit or post-abort, so the callback
/// functions can only do noncritical cleanup.
Register a closure to be called during one of the `PgXactCallbackEvent` events.  Multiple
closures can be registered per event (one at a time), and they are called in the order in which
they were registered.

Registered callbacks only remain registered for the life of a single transaction.  Registration
of permanet callbacks should be done through the unsafe `pg_sys::RegisterXactCallback()` function.
Register a closure to be called during one of the ` PgXactCallbackEvent ` events . Multiple closures can be registered per event ( one at a time ) and they are called in the order in which they were registered . Registered callbacks only remain registered for the life of a single transaction . Registration of permanet callbacks should be done through the unsafe ` pg_sys :: RegisterXactCallback () ` function .
pub fn register_xact_callback<F>(which_event: PgXactCallbackEvent, f: F) -> XactCallbackReceipt
where
    F: FnOnce() + std::panic::UnwindSafe + std::panic::RefUnwindSafe + 'static,
{
    // our map of xact callbacks.  It starts as None and gets initialized below in maybe_initialize()
    static mut XACT_HOOKS: Option<CallbackMap> = None;
    // internal function that we register as an XactCallback
    unsafe extern "C" fn callback(event: pg_sys::XactEvent, _arg: *mut ::std::os::raw::c_void) {
        let which_event = PgXactCallbackEvent::translate_pg_event(event);
        let hooks = match which_event {
            // pgx's XactCallbacks are per-transaction, so when the transaction is over
            // (that's either Commit or Abort, which are mutually exclusive), we replace our
            // const XACT_HOOKS with a new, empty Map so that subsequent transactions won't accidentally run
            // these hooks again.
            //
            // Note that we still run any hooks that are registered for these events in this xact
            PgXactCallbackEvent::Commit
            | PgXactCallbackEvent::Abort
            | PgXactCallbackEvent::ParallelCommit
            | PgXactCallbackEvent::ParallelAbort => XACT_HOOKS
                .replace(HashMap::new())
                .expect("XACT_HOOKS was None during Commit/Abort")
                .remove(&which_event),
            // not in a transaction-end event, so just borrow our map
            _ => XACT_HOOKS
                .as_mut()
                .expect("XACT_HOOKS was None")
                .remove(&which_event),
        };
        // if we have a vec of hooks for this event they're consumed here and executed
        // in the order they were registered
        if let Some(hooks) = hooks {
            for hook in hooks.into_iter() {
                // TODO:  do we need to catch panics and do something with them?  They'll cause
                //  the Postgres backend to Abort() if we're handling XactEvent::Commit/Abort events
                // effectively 'take' the hook from the internal RefCell
                if let Some(hook) = hook.replace(None) {
                    // and execute it under guard for proper panic/elog(ERROR) handling
                    crate::guard::guard(hook.0);
                }
            }
        }
    }
    // internal function to manage initialization of our transaction callback
    fn maybe_initialize<'a>() -> &'a mut CallbackMap {
        unsafe {
            // if this is our first time here since the Postgres backend started, XACT_HOOKS will be None
            if XACT_HOOKS.is_none() {
                // so lets swap it out with a new HashMap, which will live for the duration of the backend
                XACT_HOOKS.replace(HashMap::new());
                // and register our single callback function (internally defined above)
                pg_sys::RegisterXactCallback(Some(callback), std::ptr::null_mut());
            }
            XACT_HOOKS
                .as_mut()
                .expect("XACT_HOOKS was None during maybe_initialize") // this should never happen
        }
    }
    // get a mutable reference to XACT_HOOKS
    let hooks = maybe_initialize();
    // wrap the user-provided closure as an optional, reference counted cell
    let wrapped_func = Rc::new(RefCell::new(Some(XactCallbackWrapper(Box::new(f)))));
    // find (or create) the map Entry for the specified event and add our wrapped hook to it
    let entry = hooks.entry(which_event).or_default();
    entry.push(Rc::clone(&wrapped_func));
    // give the user the ability to unregister
    XactCallbackReceipt(wrapped_func)
}
pub fn register_xact_callback < F > ( which_event : PgXactCallbackEvent , f : F ) -> XactCallbackReceipt where F : FnOnce ( ) + std :: panic :: UnwindSafe + std :: panic :: RefUnwindSafe + ' static , { // our map of xact callbacks.  It starts as None and gets initialized below in maybe_initialize() static mut XACT_HOOKS : Option < CallbackMap > = None ; // internal function that we register as an XactCallback unsafe extern " " fn callback ( event : pg_sys :: XactEvent , _arg : * mut :: std :: os :: raw :: c_void ) { let which_event = PgXactCallbackEvent :: translate_pg_event ( event ) ; let hooks = match which_event { // pgx's XactCallbacks are per-transaction, so when the transaction is over // (that's either Commit or Abort, which are mutually exclusive), we replace our // const XACT_HOOKS with a new, empty Map so that subsequent transactions won't accidentally run // these hooks again. // // Note that we still run any hooks that are registered for these events in this xact PgXactCallbackEvent :: Commit | PgXactCallbackEvent :: Abort | PgXactCallbackEvent :: ParallelCommit | PgXactCallbackEvent :: ParallelAbort => XACT_HOOKS . replace ( HashMap :: new ( ) ) . expect ( " " ) . remove ( & which_event ) , // not in a transaction-end event, so just borrow our map _ => XACT_HOOKS . as_mut ( ) . expect ( " " ) . remove ( & which_event ) , } ; // if we have a vec of hooks for this event they're consumed here and executed // in the order they were registered if let Some ( hooks ) = hooks { for hook in hooks . into_iter ( ) { // TODO:  do we need to catch panics and do something with them?  They'll cause //  the Postgres backend to Abort() if we're handling XactEvent::Commit/Abort events // effectively 'take' the hook from the internal RefCell if let Some ( hook ) = hook . replace ( None ) { // and execute it under guard for proper panic/elog(ERROR) handling crate :: guard :: guard ( hook . 0 ) ; } } } } // internal function to manage initialization of our transaction callback fn maybe_initialize < ' a > ( ) -> & ' a mut CallbackMap { unsafe { // if this is our first time here since the Postgres backend started, XACT_HOOKS will be None if XACT_HOOKS . is_none ( ) { // so lets swap it out with a new HashMap, which will live for the duration of the backend XACT_HOOKS . replace ( HashMap :: new ( ) ) ; // and register our single callback function (internally defined above) pg_sys :: RegisterXactCallback ( Some ( callback ) , std :: ptr :: null_mut ( ) ) ; } XACT_HOOKS . as_mut ( ) . expect ( " " ) // this should never happen } } // get a mutable reference to XACT_HOOKS let hooks = maybe_initialize ( ) ; // wrap the user-provided closure as an optional, reference counted cell let wrapped_func = Rc :: new ( RefCell :: new ( Some ( XactCallbackWrapper ( Box :: new ( f ) ) ) ) ) ; // find (or create) the map Entry for the specified event and add our wrapped hook to it let entry = hooks . entry ( which_event ) . or_default ( ) ; entry . push ( Rc :: clone ( & wrapped_func ) ) ; // give the user the ability to unregister XactCallbackReceipt ( wrapped_func ) }
Register a closure to be called during one of the `PgXactCallbackEvent` events.
Register a closure to be called during one of the ` PgXactCallbackEvent ` events .
which_event
PgXactCallbackEvent
f
F
which_event
PgXactCallbackEvent
f
F
examples
Register a number of events for pre-commit and commit.



:PreCommit, || info!("pre-commit #1"));
register_xact_callback(PgXactCallbackEvent::PreCommit, || info!("pre-commit #2"));
register_xact_callback(PgXactCallbackEvent::PreCommit, || info!("pre-commit #3"));
register_xact_callback(PgXactCallbackEvent::Commit, || info!("called after commit"));
```

Register an event, do some work, and then decide the callback isn't actually necessary anymore.



do some initialization work

let receipt = register_xact_callback(PgXactCallbackEvent::Abort, || {  do cleanup if xact aborts */}).

do work that might abort the transaction

if we got here, the transaction did not abort, so we no longer need to care about cleanup
receipt.unregister_callback();
```
Register a number of events for pre - commit and commit . : PreCommit || info! ( " pre - commit #1 " )) ; register_xact_callback ( PgXactCallbackEvent :: PreCommit || info! ( " pre - commit #2 " )) ; register_xact_callback ( PgXactCallbackEvent :: PreCommit || info! ( " pre - commit #3 " )) ; register_xact_callback ( PgXactCallbackEvent :: Commit || info! ( " called after commit " )) ; ` ` ` Register an event do some work and then decide the callback isn ' t actually necessary anymore . do some initialization work let receipt = register_xact_callback ( PgXactCallbackEvent :: Abort || { do cleanup if xact aborts * / } ) . do work that might abort the transaction if we got here the transaction did not abort so we no longer need to care about cleanup receipt . unregister_callback () ; ` ` `
safety
Any kind of Rust `panic!()` or Postgres `ereport(ERROR)` while executing a `PgXactCallbackEvent::Commit`
or `PgXactCallbackEvent::Abort` event will immediately cause the Postgres backend to abort and
the entire cluster to restart.

As the Postgres internal documentation says.

At transaction end, the callback occurs post-commit or post-abort, so the callback
functions can only do noncritical cleanup.
Any kind of Rust ` panic! () ` or Postgres ` ereport ( ERROR ) ` while executing a ` PgXactCallbackEvent :: Commit ` or ` PgXactCallbackEvent :: Abort ` event will immediately cause the Postgres backend to abort and the entire cluster to restart . As the Postgres internal documentation says . At transaction end the callback occurs post - commit or post - abort so the callback functions can only do noncritical cleanup .